← ST-Computer 09 / 1987

Floppy-Spielereien Teil 3: Durch dunkle KanÀle

Kurs

Bevor der Floppykurs so richtig in die Vollen geht, drehen wir noch eine theoretische Ehrenrunde. Es geht um das Drumherum des Floppycontrollers, dessen Programmierung sich bis jetzt kaum jemand zutraut. Aber vielleicht wird dank dieser Serie bald der Kopierschutz und die Datenpackerei auf der Diskette zum Volkssport - wenn Sie ein bißchen durchhalten.

Zur UnterstĂŒtzung Ihrer eigenen Experimente gibt es eine Erweiterung fĂŒr den Diskmonitor MINIMON, den ich in der letzten ST vorgestellt habe. Sie wird aus PlatzgrĂŒnden leider erst im nĂ€chsten Heft abgedruckt. Die Schwierigkeit dabei ist, daß Floppyprogrammierung nur in Assembler denkbar ist - aber keine Angst, Sie mĂŒssen sich nicht monatelang mit moveq, lea und Konsorten herumschlagen. Hauptsache ist, daß Sie die Assemblerroutinen, die in das BASIC-Programm eingebunden werden, anwenden können. Und dazu muß ich wohl oder ĂŒbel auch ein wenig trockene Theorie erklĂ€ren.

"Kontrola ... was’n das?"

Das mögen sich einige Leser fragen, die bis jetzt in diesem Kurs noch nicht dabei waren oder die nicht so recht aufgepaßt haben. Der Floppycontroller ist ein Chip, der nur fĂŒr den Datenaustausch mit dem Laufwerk zustĂ€ndig ist. Er entlastet den Prozessor, indem er ihm die lĂ€stige Arbeit abnimmt, die Diskettendaten zu ver- und entschlĂŒsseln sowie sie zu lesen und zu schreiben.

ZunĂ€chst einige Worte ĂŒber die Aufzeichnung. Daß eine Diskette in Spuren und Sektoren aufgeteilt ist, wissen Sie. Sehen wir uns so eine Spur an (siehe Bild 1).

Bild 1: Ein nackter RĂŒcken kann auch entzĂŒcken
Bild 1: Ein nackter RĂŒcken kann auch entzĂŒcken

Sie sehen, daß Sie nichts sehen. Das ist auch ganz in Ordnung so, denn schließlich haben wir der Spur noch keine Informationen fĂŒr ihr kĂŒnftiges Leben mitgegeben. Nun könnten Sie sich vorstellen, die Bits wĂŒrden einfach nacheinander auf die Spur gekleistert: Magnetisierter Fleck fĂŒr „1“, nicht magnetisierter fĂŒr „0“. Nicht schlecht fĂŒr den Anfang, aber unbrauchbar. Denn so eine Spur ist rund, und jetzt erzĂ€hlen Sie mir mal, wo ein Kreis beginnt. Eine Markierung anbringen, nach jeder Umdrehung des Laufwerksmotors einen Indeximpuls erzeugen? Gute Idee, wird auch tatsĂ€chlich praktiziert, nur reicht das noch nicht. Ein Laufwerk kann mit vernĂŒnftigem technischen Aufwand nicht dazu gebracht werden, immer gleich schnell zu drehen.

TAKTLOSIGKEITEN

Irgendwie muß man also den Lesevorgang mit der Geschwindigkeit des Laufwerks synchronisieren. Ein Verfahren dazu heißt MFM, es wird beim ST und bei den meisten anderen Rechnern angewendet und codiert den Taktimpuls des Controllers mit in die Datenbits ein. Schreiben wir einmal einen solchen Track (siehe Bild 2).

Bild 2: Bit fĂŒr Bit - Schreiben in Salamitaktik
Bild 2: Bit fĂŒr Bit - Schreiben in Salamitaktik

Jetzt können wir also einwandfrei Bits lesen, ohne taktlos zu werden. Geschafft? Denkste. Wie erkenne ich denn jetzt, wo ein bestimmtes Byte anfĂ€ngt? NatĂŒrlich, Indeximpuls abwar-ten, Bits lesen und dabei immer bis 8 Bits (=1 Byte) zĂ€hlen, bis man beim gewĂŒnschten Byte ankommt... Nein, das ist es immer noch nicht. Man will ja nicht immer den ganzen Track lesen, sondern vielleicht nur einen Sektor, und außerdem könnte es ja sein, daß die Synchronisation mit dem Indeximpuls nicht ganz so glatt lĂ€uft; dann fĂ€ngt der Controller beim falschen (zum Beispiel beim zweiten) Bit zu lesen und zu zĂ€hlen an. Ergebnis: Lesefehler, BombenanschlĂ€ge, intergalaktische Kollisionen.

In der Praxis schreibt man spezielle Bytes auf die Spur, die Synchronisationsbytes. Diese Bytes erkennt der Controller daran, daß sie ohne Taktinformationen geschrieben werden. Meistens schreibt man mehrere Sync-Bytes hintereinander, und der Controller verschlingt solange Bits, bis er mal einen gĂŒltigen Wert fĂŒr ein Sync-bytes ($A1) erkannt hat. Dann weiß er: Hoppla, das nĂ€chste Bit ist der Anfang eines Bytes. Hier also unser Track, wie er wirklich aussieht (das heißt, immer noch etwas verbogen und nicht so schön rund wie in Wirklichkeit) (siehe Bild 3 ).

Bild 3: Die Wahrheit kommt ans Licht
Bild 3: Die Wahrheit kommt ans Licht

Die Markierungen der einzelnen Bits habe ich der Übersicht wegen weggelassen. „Gap“ bezeichnet einen Abschnitt von LĂŒckenbytes, die der Controller braucht, um sich zwischen den Daten zu erholen und klar Schiff zu machen. Dabei ist er allerdings fixer, als Atari und sogar sein Hersteller ihm Zutrauen; darauf beruht meine Idee, die LĂŒckenbytes soweit zu kĂŒrzen, bis ein elfter Sektor auf die Spur paßt HYPERFORMAT).

Nach den Syncbytes folgen die sogenannten Adreßmarken, mit denen bestimmte Datenstrukturen angekĂŒndigt werden (Datenheader, also Vorspann oder Daten). Auch sie werden ohne Taktimpulse geschrieben. Nach den Daten- bzw. Vorspannblöcken folgt jeweils eine 2-Byte-Checksumme, mit der man die GĂŒltigkeit der Informationen nachprĂŒfen, aber auch jede Menge Unsinn treiben kann.

HACKORDNUNGEN ODER DER NAME DER DOSE

Die ganze Lese-/Schreibarbeit und die CodierungsvorgĂ€nge werden dem Abt des Klosters Atari ST, Prozessorus Maximus, von seinem Schreiberling Flopius Controllus abgenommen. Nun ist aber Prozessorus auch noch zu bequem, um Controllus die Daten selbst zu bringen oder sie abzuholen: Er setzt einen Boten ein, D.M.A. Controllus genannt, der speziell fĂŒr Datenschaufeleien dieser Art angeheuert wurde. Ernst beiseite: Der DMA-Controller (Direct Memory Access) bekommt beim Schreiben vom Prozessor die Instruktion, was der Floppycontroller tun soll. Hier dient er als Relaisstation; außerdem bekommt er eine Speicheradresse, wo die Informationen stehen, die er dem Controller HĂ€ppchen fĂŒr HĂ€ppchen selbstĂ€ndig ĂŒbermitteln soll. Der Prozessor zieht sich solange wĂŒrdevoll zurĂŒck, um das Ende der Übertragung abzuwarten, das er entweder im Erreichen einer bestimmten DMA-Adresse oder an einer Unterbrechungsanforderung des Controllers merkt. Das Lesen funktioniert analog.

Warum so ein Aufwand? WĂ€hrend der DMA-Chip sich um die Speicherschaufelei kĂŒmmert, kann der Prozessor sich seiner Freizeit oder wichtigeren AktivitĂ€ten widmen. Vor allem fĂŒr Multitasking-Betriebssysteme ist das ĂŒberlebenswichtig. Nur leider nĂŒtzt TOS diese Möglichkeiten nicht aus und verbrĂ€t die kostbare Prozessorzeit in Warteschleifen, solange gelesen und geschrieben wird. In RTOS dagegen kann man sogar wĂ€hrend des Formatierens noch andere Dinge erledigen! Ein Schema soll die folgenden, recht kniffligen AusfĂŒhrungen erhellen:

( siehe Bild 4 ).

Es zeigt alle Chips, die bei der Ein-/ Ausgabe auf Diskette zusammenspielen. Nicht nur Prozessor, DMA-Controller und Floppycontroller sind -wie man sieht - am Lesen/Schreiben beteiligt. Die Unterbrechungsanforderung des Floppycontrollers, mit der er vehement auf das Ende seiner Arbeit hinweist, gelangt zum MFP68901, einem Baustein im ST, der unter den Chips des ST nur „der Störenfried“ heißt, weil er stĂ€ndig andere bei der Arbeit unterbricht: Er verwaltet alle Interruptanforderungen der Peripherie.

Dazu kommt noch, daß der Controller eigentlich nur ein Laufwerk ansprechen kann - und sogar nur eine Seite! Aber hilfsbereit, wie er ist, springt da der Soundchip ein und leiht dem Controller drei seiner Portbits und schaltet mit ihnen je nach Bedarf zwischen den Seiten und den Laufwerken (maximal zwei) um.

Wie diese Chips Zusammenwirken und wie man sie dazu bringt, sich gegenseitig zu unterstĂŒtzen, wird uns nun beschĂ€ftigen. Die Dreh- und Angelpunkte beim Diskzugriff sind Floppycontroller (auch FDC fĂŒr Floppy Disk Controller) und DMA-Controller.

GONDELFAHRT DURCH DEN SPEICHER

Die Aufgaben des DMA-Chips wurden ja schon kurz erwĂ€hnt: Er hievt Daten vom Speicher zum Floppycontroller (oder auch zum Controller der Festplatte) und von dort aus wieder in den Speicher, ohne daß sich der Prozessor darum kĂŒmmern muß. Der Prozessor gibt dem DMA-Chip seine Anweisungen, was er gerne da und dort hĂ€tte, und beschĂ€ftigt sich dann lieber damit, Invaders oder StripPoker zu spielen.

Die Anweisungen des Prozessors landen in ein paar Adressen, die ich jetzt in ihre Bits zerlegen werde. Auch die Ausgabe des DMA-Chips (Statusmeldungen und Ă€hnliches) lĂ€uft ĂŒber diese Adressen (siehe Tabelle 1).

Adresse Bedeutung Tabelle 1
$FF8604REGISTERZUGRIFF
Nur die unteren 8 Bits sind belegt. Aus dieser Adresse kann man den Inhalt von DMA-Chip- und Floppycontroller-Registern lesen oder man kann sie durch diese Adresse verÀndern. Welches Register ausgewÀhlt ist, hÀngt von der folgenden Adresse ab.
$FF8606DMA-Modus, DMA-Status
FĂŒr den Schreibzugriff gilt folgende Belegung:
BitWirkung
0keine erkennbare Bedeutung, rÀtselhaft
1,2Wenn Bit 4 gelöscht ist, kann man mit diesen Bits die Register des Floppycontrollers auswÀhlen, auf die man dann in SFF8604 zugreifen kann. Dabei spielt auch der Zustand des Schreib-/Lesebits (Bit 8) eine Rolle:
Bit 1,2 angesprochenes FDC-Register
0 0 beim Lesen Statusregister, sonst Kommandoregister
1 0 Spurregister
0 1 Sektorregister
1 1 Datenregister
3Datenaustausch mit dem Floppycontroller (Bit gelöscht) oder mit der Harddisk (Bit gesetzt)
4Ist das Bit gelöscht, kann man in $FF8604 die Register des FDC manipulieren, ansonsten den „SektorzĂ€hler“ des DMA-Chips (siehe unten).
5Mysteriös
6Bit gelöscht: DMA an, Bit gesetzt: DMA macht Ferien
7dasselbe wie Bit 3, nur in grĂŒn: die Bitbedeutung ist gerade vertauscht. Der Sinn dieses Doppel-Moppels ist mir schleierhaft.
8Bit gelöscht: Controllerregister lesen, sonst: Controllerregister beschreiben.

Beim Lesen sind nur 3 Bits erkennbar belegt, die restlichen melden sich zwar des öfteren als gesetzt, was sie aber bedeuten, konnte ich noch nicht eruieren:

0 Fehler bei DMA? (Bit gesetzt bedeutet ja)
1 Zustand des SektorzĂ€hlers (Bit gesetzt bedeutet, daß der SektorzĂ€hler noch nicht bei 0 angekommen ist; dadurch kann man unvollstĂ€ndige DatenĂŒbertragungen erkennen)
2 „REQUEST“-Bit. Hier telefoniert der Floppycontroller durch, ob er auf Daten vom DMA-Chip wartet oder welche loswerden will.

$FF8609DMA-ZĂ€hler, Highbyte. Hier liegen die obersten 8 Bit eines ZĂ€hlers, der angibt, welche Adresse vom DMA-Chip gerade bearbeitet wird.
$FF860BDMA-ZĂ€hler, Midbyte. Die mittleren 8 Bit.
$FF860DDMA-ZĂ€hler, Lowbyte. Die unteren 8 Bit.

WIR KÖCHELN UNS EIN DATENBRÄU

Wie geht man mit diesem Wust nun um? Einzelne Register des Floppycontrollers anzusprechen, ist ja noch relativ einfach: Man schreibt zum Beispiel den Wert $80 in das Modusregister des DMA-Chips. Damit hat man das Statusregister des Floppycontrollers ausgewĂ€hlt, das man dann im Zugriffsregister $FF8604 auslesen kann. Anderes Beispiel: $184 ins Modusregister, und schon kann man in $FF8604 das Sektorregister beschreiben (halten Sie sich immer die Tabelle 1 vor Augen!). Und so weiter. Nehmen wir an, wir wollten jetzt den Speicherbereich von JETZT__GEHTS__ABER—LOS bis DA__HÖRTS__ABER—AUF auf die Diskette schreiben, beispielsweise als Sektor. Kompliziert, denken Sie? Irrtum. SEHR kompliziert. Das Kochrezept dafĂŒr.

  1. Startadresse der Übertragung (also JETZT__GEHTS__ABER__LOS) nacheinander (und in dieser Reihenfolge) in die DMA-ZĂ€hlerbytes (Low-byte, Midbyte, Highbyte) eintragen. Nun weiß der DMA-Chip, wo die Übertragung losgeht.
  2. Zur Sicherheit: Den Status des DMA-Chips löschen. Wie man das macht, muß einem allerdings gesagt werden: Dazu „klappert“ man mit der Schreib-/Leseleitung (Bit 8 des DMA-Modus-Registers), das heißt, man wechselt schnell hintereinander deren Zustand. Wir wollen auf die Diskette schreiben, also legen wir die Leseleitung zuerst auf 1, dann auf 0 und dann wieder mit Schritt 3 auf 1.
  3. Hier beschreiben wir nĂ€mlich das DMA-Modusregister mit der Instruktion, die wir brauchen. In unserem Fall wollen wir mit dem DMA-Chip Daten ĂŒbertragen. Wo er mit der Übertragung anfangen soll, weiß er bereits. Aber wann hört er auf? Das mĂŒssen wir ihm noch in sein goldenes Buch, den SektorzĂ€hler schreiben. In den SektorzĂ€hler schreibt man die Anzahl der zu ĂŒbertragenden Sektoren (ein Sektor besteht auch hier aus 512 Bytes), der DMA-Chip zĂ€hlt dieses Register dann selbstĂ€ndig wĂ€hrend der Übertragung herunter. Indem Sie $190 ins DMA-Modus-Register schreiben, wĂ€hlen Sie den SektorzĂ€hler des DMA-Chips zum Beschreiben aus.
  4. Diesem SektorzĂ€hler ĂŒbergeben wir in $FF8604 nun die Maximalzahl von Sektoren, die der DMA-Chip ĂŒbertragen soll. Bei uns wĂ€re das 1 + ( DA_HÖRTS__ABER__AUF - JETZT__GEHTS__ABER__LOS ) / 512, die 1 wird addiert, um auch die paar ĂŒberzĂ€hligen Bytes noch loszuwerden, falls die Divison nicht glĂŒcklich und ganzzahlig ausgeht (und wo gibt es schon noch ein happy end?).
  5. Auf Ă€hnliche Weise sagen wir dem Floppycontroller, welchen Sektor er auf der Diskette beschreiben soll. $184 ins DMA-Modusregister, und das Sektorregister des FDC liegt uns in SFF8604 fertig zum heftigen Beschreiben zu FĂŒĂŸen. Was wir dann auch tun.
  6. Und jetzt wĂ€hlt man das Kommandoregister des FDC aus ($180 ins DMA-Modus-Register) und ĂŒbergibt ihm in $FF8604 einen Read-Sector-Befehl.

NatĂŒrlich war’s das noch lange nicht. Jetzt mĂŒĂŸte man noch das Ende der Übertragung abwarten. Das macht man entweder, indem man die Unterbrechungsanforderung des Floppycon-trollers abwartet, mit der er Fehler oder das Ende seiner MĂŒhsal anzeigt, oder indem man den DMA-ZĂ€hler beĂ€ugt, abwartet, bis die Übertragung bei einer bestimmten Adresse angelangt ist (in unserem Fall bei DA__HÖRTS__ABER__AUF) und dann den Controller brutal abwĂŒrgt (dazu gibt es einen speziellen Strangulationsbefehl). Und wer ganz sauber arbeitet, sollte sich danach noch den Status von DMA-Chip und Floppycontroller anschauen, um eventuelle Fehler abzufangen...

Damit Sie sich schnell zurechtfinden und nicht immer in die einzelnen Bits gehen mĂŒssen, habe ich in einer Tabelle die Werte zusammengefaßt, die man in die DMA-Modus-Adresse schreiben muß, um bestimmte Register auszuwĂ€hlen. Hier die Tabelle fĂŒr das Lesen von Registern:

Wert angesprochenes Register
$80 Statusregister des Floppycontrollers
$82 Spurregister des FDC (Floppycontroller)
$84 Sektorregister des FDC
$86 Datenregister des FDC
$90 SektorzÀhler des DMA-Chips

Und die Tabelle fĂŒr den Schreibzugriff auf Register:

$180 Kommandoregister des FDC
$182 Spurregister des FDC
$184 Sektorregister des FDC
$186 Datenregister des FDC
$190 SektorzÀhler des DMA-Chips

Eine kleine humoristische Einlage des DMA-Chips sollte noch ErwĂ€hnung finden: Liest man weniger als 16 Bytes ein, tut sich gar nichts. Woran liegt’s? Der DMA-Chip puffert intern 16 Bytes, bevor er sie in den Speicher schreibt. Wenn wir also 167 Bytes zu lesen wĂŒnschen, liefert der DMA-Chip 160 (zehnmal 16) und behĂ€lt 7. Dieser Puffer wird ĂŒbrigens gelöscht, wenn Sie den Status per Schreib-/ Leseleitung-Klappern löschen...

Beim Schreiben von Datenmengen (also nicht von Registern) benimmt sich der DMA-Chip noch eigenwilliger. Man ist nicht etwa dann mit dem Schreiben eines Sektors fertig, wenn die aktuelle DMA-Adresse 512 Bytes höher ist als die Startadresse, sondern erst bei 512 + 32 Bytes! Zum GlĂŒck spinnt nur der DMA-ZĂ€hler und nicht die DatenĂŒbertragung selbst, so daß man diesen Fehler leicht umgehen kann: Man wartet einfach ein bißchen lĂ€nger (eben bis laut ZĂ€hler 512 + 32 Bytes ĂŒbertragen sind).

Wenn Sie das alles auf Anhieb verstanden haben, sind Sie wahrscheinlich an der Entwicklung des DMA-Chips beteiligt gewesen. Schauen Sie sich mal im Listing 1 (LOCKSLEY.S) die Routine wrsec an, mit der man Sektoren schreibt. Dort werden Sie wiederfinden, was ich Ihnen erklÀrt habe. Noch eine Anmerkung: Bilden Sie sich nicht ein, Sie könnten diese direkte Programmierung des DMA-Chips effektiv und effizient in einer Hochsprache erledigen - wenn es eine Hochburg der Assemblerprogrammierung gibt, dann liegt sie in der Floppyprogrammierung, wo es zeitlich ziemlich knapp wird.

Aber geben Sie jetzt nicht auf, weil ihre einzigen Fremdsprachen Englisch und BASIC sind. FĂŒr Sie stelle ich im nĂ€chsten Teil des Floppykurses eine Erweiterung fĂŒr den MINIMON vor, mit dem Sie all diese Dinge von BASIC aus erledigen können. Bevor ich dazu komme, bringen wir noch schnell die Funktionen von Soundchip und MFP beim Diskzugriff hinter uns.

Bild 4: Die MaikÀfer kommen!
Bild 4: Die MaikÀfer kommen!

LUSTVOLLES STÖHNEN NACH FEIERABEND

Wenn der Controller fix und ferne mit der Welt ist, also sein Kommando abgearbeitet hat, meldet er das, indem er eine Unterbrechungsanforderung auslöst. Die meldet er dem MFP (Sie wissen schon, der ungemĂŒtliche Zeitgenosse von vorhin). Wie fragt man das ab? Ganz einfach:

btst #5,mfp  
beq fertig

Dieser Assemblerbrocken testet Bit 5 des I/O-Port im MFP (Adresse $FFF-A01). Wenn das erschöpfte ’Fertig’-Keuchen des Controllers durch die Chiplandschaft hallt, meldet das der MFP, indem er dieses Bit auf 0 setzt. Wie das im Programm aussieht, sehen Sie in der Routine WARTEN_AUF__GODOT von LOCKSLEY.S (siehe Listing 1).

STABILE SEITENLAGE IM LAUFWERK

Beim Soundchip sind im Grunde nur zwei Adressen interessant. Die eine heißt schlicht SND (zumindest in meinem Select-Programm), liegt bei $FF-8800 und ist das Auswahlregister des Chips. Das heißt, man schreibt die Nummer des gewĂŒnschten Soundchipregisters in diese Adresse hinein und kann dann entweder deren Inhalt aus SND lesen oder das angewĂ€hlte Register verĂ€ndern, indem man in $FF8802 (SNDWRT fĂŒr „Soundchip-Write-Eingang“) einen Wert schreibt. Das Register des Soundchips, das uns interessiert, ist das vierzehnte. Darin liegt der Port A des Chips:

Belegung von Port A des Soundchips

Bit Bedeutung
0 Seitenauswahl bei der Floppy
1 Auswahlsignal fĂŒr Laufwerk A
2 Auswahlsignal fĂŒr Laufwerk B

Die restlichen Bits interessieren uns nicht. Bevor wir also den Floppycontroller selbst ansprechen (bzw. den DMA-Controller), mĂŒssen wir erst im Soundchip Seite und Laufwerk einstellen. Auch das finden Sie in einem Programm des Floppykurses, in SELECT.S.

So, jetzt reicht’s aber wirklich mit Adressen und Bits und Kommandos und derlei Verwirrendem mehr. Im Grunde habe ich das alles nur fĂŒr diejenigen Verwegenen herausgearbeitet, die sich selbst an die Programmierung machen wollen, obwohl ich doch in dieser Ausgabe der ST eine Sammlung von Routinen vorstelle, die jeder abkupfern und zum Vorbild nehmen kann. NatĂŒrlich können Sie sich auch noch selbst bemĂŒhen; dazu gibt es im nĂ€chsten Teil des Floppykurses relativ komplette Informationen ĂŒber den Floppycontroller (die hĂ€tten diesmal alle Heftgrenzen gesprengt).

FRAGEN ÜBER FRAGEN

Seit der ersten Folge des Floppykurses rennt man mir telefonisch und brieflich die Bude ein. Das ist auch ganz in Ordnung so, denn dabei kann ich ja auch noch lernen. Nur hĂ€ufen sich gewisse Fragen, und ich nehme an, daß sie von allgemeinem Interesse sind:

Frage 1: Kann man mit HYPERFORMAT auch Festplatten formatieren? Die Antwort: Nein, nein, nein. Erstens nimmt HYPERFORMAT den Floppycontroller in die Mangel und nicht den Festplattencontroller. Zudem ist der bei den verschiedenen Festplatten, die fĂŒr den ST angeboten werden, auch noch unterschiedlich. Und drittens wĂ€re mir auf der Festplatte Datensicherheit wirklich wichtiger als 20 oder 30 Prozent mehr KapazitĂ€t, finden Sie nicht?

Frage 2: Gibt es neue Versionen von HYPERFORMAT und wo bekomme ich sie? Ich arbeite immer noch recht hĂ€ufig an HYPERFORMAT, weil mir immer wieder etwas dazu einfĂ€llt. Die aktuelle Version ist jetzt (Ende Juni) 2.2 mit Tendenz zur 2.3. Sie hat eine zusĂ€tzliche Verifyoption und ein paar kleinere Verbesserungen mitbekommen. Zu bekommen ist die jeweils neueste Version mit einigen Extras gegen frankierten RĂŒckumschlag und Diskette sowie 10 DM unter meiner Adresse, die Sie in den Fistings finden.

Frage 3: Wie kompatibel sind HYPERFORMAT-Disketten zu anderen Rechnern? Knifflig. Ich könnte mir gut vorstellen, daß Sie HYPERFORMAT auf vielen anderen Laufwerken physikalisch lesen können. Das hĂ€ngt sehr stark vom Floppycontroller ab, aber da die meisten nach dem MFM-Standard arbeiten, habe ich da eine gewisse Hoffnung. Das logische Format ist natĂŒrlich je nach Rechner völlig verschieden. Am Ă€hnlichsten sind Disketten, die unter MS-DOS formatiert wurden. Insofern besteht Hoffnung, falls Atari doch einmal in ferner Zukunft den MS-DOS-Emulator bringt. Vielleicht vertiefe ich mich dann auch noch einmal in die Materie und produziere ein HYPERFORMAT fĂŒr MS-DOS-Rechner... Wo wir gerade bei KompatibilitĂ€ten sind: FĂŒr das Blitter-TOS werde ich eine neue Version von HYPERFORMAT herausbringen. Wie sich HYPERFORMAT mit dem AMIGA vertrĂ€gt, konnten Sie ja schon in der letzten Folge erfahren.

Frage 4: HYPERFORMAT produziert bei mir Disketten, auf die ich nicht mehr schreiben kann. Tja, das kann viele Ursachen haben. Erstens sollten Sie lieber zweimal nachprĂŒfen, ob Sie auch eine lauffĂ€hige Version haben, das heißt, ob Sie nicht vielleicht doch einem Tippfehler erlegen sind oder etwas beim Kopieren schiefgegangen ist. Dann sollten Sie sicher sein, daß Sie HYPERFORMAT auch richtig bedienen (siehe ST 6/87). Und lĂ€uft es dann noch nicht, gibt es immer noch mehrere Möglichkeiten: Verwenden Sie schlechte Disketten? PrĂŒfen Sie mal nach, ob mit hochwertigen Disketten nicht was zu machen ist. Dreht Ihr Laufwerk zu schnell? Normal ist eine Umdrehungszahl zwischen 300 und 304 Umdrehungen pro Minute (herauszufinden mit dem Public-domain-Programm SPEED.TOS oder auch mit dem neuen COPYSTAR).

Trifft das alles nicht zu, dann gehören Sie wahrscheinlich zu den vom Schicksal benachteiligten ST-Besitzern, deren Controller HYPERFORMAT (noch) nicht vertrĂ€gt. Aber selbst dann ist noch nicht aller Tage Abend. Oft hilft es, wenn Sie nicht alle Tracks formatieren, die Sie erreichen können, sondern nur bis Spur 80. Wenn Sie ein Sourcelisting haben, vorzugsweise das der Version 2.1, versuchen Sie die LĂŒckenbytes zu Ă€ndern (in der Version 2.1 ganz am Anfang des Listings als gap1 bis gap4 durchparametriert), das Programm zu assemblieren und damit zu formatieren. Wenn Sie das nicht können, fragen Sie einen Freund oder schreiben Sie mir. Wenn Sie selbst Probleme mit HYPERFORMAT hatten und sie irgendwie gelöst haben, lassen Sie es doch die Welt wissen: Schreiben Sie mir oder der „ST-Computer“ ĂŒber Ihre Erfahrungen - geben Sie dabei bitte genau Ihre Systemkonfiguration an (und auch bitte das Kaufdatum Ihres ST, ich vermute, daß es verschiedene Baureihen gibt). Leider kann ich nicht ausschließen, daß trotz aller BemĂŒhungen einige ST-Besitzer auf HYPERFORMAT verzichten mĂŒssen - das Programm geht nun mal bis an physikalische Grenzen heran.

Frage 5: Was kommt als nĂ€chstes? WĂ€hrend des Kurses kommen mir immer mehr Ideen zum Thema. Darunter ist aas bereits angekĂŒndigte Kopierprogramm fĂŒr HYPERFORMAT-Disketten, ein wirklich superschnelles Formatierprogramm fĂŒr „normale“ 9- und 10-Sektordisketten, eine ganz besondere RAM-Disk, der BrĂŒckenschlag zwischen .AMIGA und ST und und und...

Bleiben Sie also dran. In der nĂ€chsten Folge werden wir uns noch einmal dem Controller und dessen Programmierung widmen. Der MINIMON wird wohl noch ein wenig wachsen (irgendwann werden wir ihn umtaufen mĂŒssen), und schließlich gibt es noch ein paar Anwendungen fĂŒr den Trackmonitor. Und den Rest behalte ich vorerst fĂŒr mich. Bis bald!

FREIHEIT FÜR DEN LESEKOPF!

Die Floppyroutinen finden Sie in Listing 1 und 2. Listing 1, LOCKSLEY.S, ist ein Assemblerprogramm, dem der Befehlscode des Controllerbefehls und einige Parameter in einem Feld ĂŒbergeben werden und das daraufhin selbsttĂ€tig die richtige Routine auswĂ€hlt und sich mit DMA, MFP und FDC herumschlĂ€gt. Listing 2, SELECT.S, dient bislang nur dazu, das richtige Laufwerk und die richtige Seite anzuwĂ€hlen, wird aber noch erweitert werden.

Ein paar Anregungen: Lesen Sie mal Spuren von verschiedenen Disketten ein (normal formatierte und HYPER-FORMATierte). Sehen Sie den Unterschied? Bei HYPERFORMAT liegen die Sektoren viel dichter beieinander. Versuchen Sie doch mal, ein eigenes Trackformat zu erzeugen. Wie wÀre es mit 16 Sektoren zu 256 Bytes (damit ist das erzeugte Format kompatibel zu bestimmten HP-Rechnern)? Oder mit 1024-Byte-Sektoren?

Schauen Sie sich auch mal - sofern Sie so etwas besitzen - eine kopiergeschĂŒtzte Diskette an und versuchen Sie herauszufinden, worin der Schutz besteht (1024-Byte-Sektoren, wild durcheinandergewĂŒrfelte SektorgrĂ¶ĂŸen, Checksummen- und andere Fehler...); das sollte ĂŒbrigens keine Aufforderung zum Raubkopieren sein, deswegen halte ich mich mit konkreten Tips zu bestimmten Programmen auch lieber zurĂŒck. Lesen Sie zur Analyse nicht nur den Track - der Read-Track-Befehl des Controllers ist nicht besonders zuverlĂ€ssig. Verwenden Sie auch Read-Address. Gehen Sie am besten so vor: Ermitteln Sie mit Read-Address, wieviele Sektoren ĂŒberhaupt vorhanden sind und wie groß diese sind. Dann lesen Sie die ganze Spur ein und vergleichen die Ergebnisse (dazu kann man in MINIMON den Drucker verwenden). Und zuguterletzt schauen Sie sich die einzelnen Sektoren an. Danach mĂŒĂŸten Sie ziemlich genau wissen, wie die Spur aussieht, auch wenn der Read-Track-Befehl nicht so funktioniert, wie er das anstĂ€ndigerweise tun mĂŒĂŸte.

LOCKSLEY, DER BEFREIER DES LESEKOPFES

Wie sein Namensgeber (Robin of Locksley, im Volksmund Robin Hood) kĂ€mpft LOCKSLEY. S fĂŒr die Freiheit von UnterdrĂŒckten, in diesem Fall von repressiv programmierten Leseköpfen unzĂ€hliger Laufwerke (Schrittmotoren aller LĂ€nder, vereinigt Euch!). LOCKSLEY.S ist einerseits eine Sammlung von Routinen, die Sie immer wieder brauchen werden, wenn Sie sich mit der direkten Floppyprogrammierung, entschlackt von der Last des Betriebssystems, befassen, und andererseits ein Modul, das wie SELECT.S vollstĂ€ndig relokatibel ist. SELECT.S wie LOCKSLEY.S sind mit dem AS68 assembliert worden. Wie man das macht? Dazu ruft man den AS68 so auf: ’AS68 — 1 -u filename.s’. Der Linker produziert dann nach 'linker filename.prg = filename.o’ ein lauffĂ€higes Programm.

Auch bei LOCKSLEY.S finden Sie am Anfang des Programms eine Tabelle von Ein-/Ausgabeparametern, die sich auch bei eventuellen Erweiterungen des Programms nicht verschieben wird. Wenn Sie LOCKSLEY.S benutzen, erwartet das Programm mindestens den Opcode desjenigen Befehls, den der Controller ausfĂŒhren soll, im Parameter 'opcode'. LOCKSLEY analysiert diesen Opcode und springt dann die richtige Routine fĂŒr jeden Befehl an. Zu den einzelnen Befehlen können noch weitere Parameter benötigt werden. Eine Übersicht der möglichen Befehle des Controllers und der Ein/ Ausgabeparameter finden Sie in der Tabelle auf der nĂ€chsten Seite. Was die einzelnen Befehle tun, finden Sie kurz in der Anleitung zum EXTEN -DED MINIMON erlĂ€utert (genaues in der nĂ€chsten Folge).

ENE MENE MISTE - DIE QUAL DER WAHL

SELECT.S ist ein Programm, das sich (bis jetzt) ausschließlich damit beschĂ€ftigt, die richtige Seite und das richtige Laufwerk auszuwĂ€hlen. Man ruft es auf, bevor man einen Controllerbefehl abschickt, damit man sicher ist, daß der Formatierbefehl, den man im Sinne hat, auch tatsĂ€chlich die freie Disk in Laufwerk B in die Mangel nimmt und nicht die Systemdiskette in Laufwerk A. VersĂ€umt man das, erzeugt das graue Haare, aber wem unter ihnen erzĂ€hle ich damit etwas Neues...

Im Quelltext erkennen Sie, daß ganz am Anfang des Programms bestimmte Speicherstellen fĂŒr die ParameterĂŒbergabe durch BASIC (oder andere Sprachen) vorgesehen sind. In ’laufwerk’ schreibt man die Bitkombination fĂŒr aktuelle Seite und gewĂŒnschtes Laufwerk (dazu schauen Sie sich am besten die Belegung des Port A im Soundchip an, die im Artikel erwĂ€hnt ist). Laufwerk A, Vorderseite wĂ€hlt man an, indem man „2“ in ’laufwerk’ schreibt und dann in die Routine springt.

Wenn Sie sich den Quelltext anschauen, werden Sie sich wundern, daß wĂ€hrend des Programms alle Interrupts ausgeschaltet werden. Das hat einen einfachen Grund. In einer Interruptroutine prĂŒft das Betriebssystem stĂ€ndig, ob auf einem Laufwerk eine Diskette gewechselt wurde. Dazu muß es die Laufwerke natĂŒrlich selektieren und benutzt dazu den Port A. Damit es da keine Kollisionen gibt, habe ich den Interrupt fĂŒr diese kurze Zeit einfach verboten.

Ein Wort noch zu einer weiteren Besonderheit. Nach einem Befehl deselektiert man sinnvollerweise das Laufwerk, indem man „0“ in ’laufwerk’ schreibt und SELECT aufruft. Allerdings kam es dabei vor, daß man das Laufwerk abwĂ€hlte und trotzdem (oder gerade deswegen) der Laufwerksmotor sich wie das Auto einer großen Marke benahm: Er lief und lief und lief...

Woran das liegt, weiß ich noch nicht genau, ich hoffe da auf die UnterstĂŒtzung eines kundigen Lesers. Was ich weiß: Wenn man abwartet, bis das Laufwerk seinen Motor von alleine abschaltet, und dann erst die Floppy de-selektiert, funktioniert es. Dabei muß man aber in Kauf nehmen, daß nach jedem Befehl eine Sekunde fĂŒr das Auslaufen des Motors verlorengeht. Diese Warteschleife finden Sie im Programm ab dem Label ’motor’.

In der nĂ€chsten Ausgabe werde ich SELECT so erweitern, daß man mit diesem Programm auch die Register des Floppycontrollers direkt von BASIC aus lesen und beschreiben kann. Deswegen sind in das Listing einige Definitionen und „open ends“ eingebaut, ĂŒber die Sie sich vielleicht wundern mögen.

(C.B.)

Die Liste der Routinen:

main Initialisierung
motor Warteschleife, bis der Motor ausgelaufen ist
mach_mal Laufwerk und Seite selektieren
super Supervisor-/Usermodus einschalten
time Warteschleife

Befehl Parameter
irq Eingabe: nur Opcode in ’opcode’ Ausgabe: keine!
Read Sektor Eingabe: Opcode in ’opcode’, Pufferadresse in ’bufferl’, Sektor in ’sector’, Anzahl der zu lesenden Bytes in ’count’ Ausgabe: gelesene Bytes in ’count’, Status des Floppycontrollers in ’fst’, DMA-Status in ’dst’, Ende der DMA-Übertragung in ’end’, Timeoutflag in ’timeout’
Read Track Eingabe: Opcode in ’opcode’, Pufferadresse in ’bufferl’, Zahl der zu lesenden Bytes in ’count’ Ausgabe: wie Read Sector
Write Track Eingabe: Opcode in ’opcode’, Adresse der Trackdaten in ’bufferl’, Zahl der zu schreibenden Bytes in ’count’ Ausgabe: geschriebene Bytes in ’count’, sonst wie bei Read Sector
Write Sector Eingabe: Opcode in ’opcode’, Adresse der Sektordaten in ’bufferl’, Zahl der zu schreibenden Bytes in ’count’, Sektornummer in ’sector’ Ausgabe: wie bei Write Track
Step, Step-in Step-out, Restore Eingabe: Opcode in ’opcode’ Ausgabe: Controllerstatus in ’fst’
Seek Eingabe: Opcode in ’opcode’, Zielspur in ’track’ Ausgabe: wie bei Step etc.
Read Address Eingabe: Opcode in ’opcode’, Zahl der zu lesenden Adreßfelder in ’count’, Pufferadresse fĂŒr die Adreßfelder in ’bufferl’, Pufferadresse fĂŒr die Statusmeldungen in ’buffer2’ Ausgabe: wie bei Read Sector
Die Liste der Routinen:
main Initialisiert, verzweigt und macht Schluß
wrfdc Ein Byte in $ff8604 schreiben (Zugriff auf FDC- oder DMA-Register )‱ as_time_goes_by Warteschleife
warten_auf_godot Wartet auf Ende des FDC-Kommandos
poll Testet auf Unterbrechungsanforderung
game_over ZeitĂŒberlauf passiert
fix_und_fertig DMA-Übertragung am Ende
Status Status des DMA-Chips lesen dma Startadresse fĂŒr DMÄ setzen super Supervisor/Usermodus umschalten analyze Analysiert den Opcode und verzweigt irq Routine fĂŒr den FORCE-IRQ-Befehl rdsec Routine fĂŒr den Read-Sector-Befehl
exe Einsprung fĂŒr den Zyklus „Kommando schreiben“-„warten“-„Status lesen“
rdtrk Routine fĂŒr den Read-Track-Befehl
wrtrk Routine fĂŒr den Write-Track-Befehl
wrsec Routine fĂŒr den Write-Sector-Befehl
step_dance Routine fĂŒr Step, Step-In, Step-Out und Restore
such_hasso Routine fĂŒr den Seek-Befehl
rdadr Routine fĂŒr den Read-Address-Befehl nochnid Schleifenmarke fĂŒr 'noch ’ne ED lesen’

LOCKSLEY.S (C) 1907 Claus Brod

*********************************************
* LOCKSLEY - Der Befreier des Lesekopfes    *
* Direkter Zugriff auf den Floppycontrol1er *
*   --- Test auf eigene Gefahr ----         *
* Mit Schnittstelle nach außen fĂŒr den      *
* Anschluß an MINIMON                       *
* Written 1987 by                           *
*   Claus Brod                              *
*   Am Felsenkeller 2                       *
*   8772 Marktheidenfeld                    *
*                                           *
* Version 1.0                               *
*   6.6.87 (starting all over again)        *
*   7.6.87 (main work)                      *
*   8.-11.6.87 (debugging)                  *
*   12.6.87 (adding some sub's)             *
*   13.6.87 (fiddling with #@!?* Read Adr.) *
*   14.-16.6.87 (cleaning up)               *
* Assembliert mit AS68                      *
*********************************************

*************************
* Ein paar Definitionen fĂŒr den langen Weg 
*************************

mfp - Sfffa01       * Adresse des MFP68901 fĂŒrs Polling

daccess= $ff8604 * DMA-Contro1ler, FDC-Zugriff oder SektorzÀhler
dmodus = $ff8606 * DMA-Contro1ler, Modus einstellen
dlow   = $ff860d * DMA-Contro1ler, Übertragungsstart Lowbyte
dmid   = $ff860b * DMA-Contro1ler, ĂŒbertragungsstart Midbyte
dhigh  = $ff8609 * DMA-Contro1ler, ĂŒbertragungsstart Highbyte
time   = $40000 * Timeout-Konstante

*************************
* Der Sprung ins Ungewisse 
*************************

bra main * Sprung zum Programmanfang

*************************
* Ein/Ausgabefeld zur ParameterĂŒbergabe 
*************************

opcode:     .dc.w 0         * Hier kommt der Opcode
*                             des Controllerbefehls hinein
track:      .dc.w 0         * Tracknummer
*
sector:     .dc.w 0         * Sektornummer
*
count:      .dc.w 0         * in:zu ĂŒbertragende Bytes/ID-fields etc.
*                             out:ĂŒbertragene Bytes
buffer1:    .dc.l 0         * Adresse des ersten Puffers fĂŒr
*                             Track-, Sektor-, ID-Daten
buffer2:    .dc.l 0         * Adresse des Reservepuffers
*
fst:        .dc.w 0         * Status des Controllers
*
dst:        .dc.w 0         * Status der DMA
*
dstart:     .dc.l 0         * Start der DMA—Übertragung
*
dend:       .dc.l 0         * Ende der DMA—Übertragung
*
timeout:    .dc.w 0         * Timeoutflag
*

stk:        .dc.l 0         * Puffer fĂŒr Stackpointer
*
dflag:      .dc.w 0         * DMA-Flag
*
***************************
* Hauptverteilerroutine 
***************************

main:
    movem.l d0-d7/a0-a6,-(sp)   * Register verschwinden lassen 
    clr.l d0                    * Userstack wird Supervisorstack
    bsr super                   * Supervisormodus an
    lea stk(pc),a2              * Adresse des Puffers fĂŒr den Stackpointer
    move.l d0,(a2)              * Stackpointer retten
    move.w opcode(pc),d6        * FDC-Befehl holen
    lea dflag(pc),a2            * Adresse des DMA-Flags
    move.w #0,(a2)              * DMA-Flag initialisieren
    lea timeout(pc),a2          * Adresse des Timeoutflags
    move.w #0,(a2)              * Timeout—Flag initialisieren
    st $43e                     * Floppy-Vertical—B1ank—Interrupt sperren
    bsr analyze                 * Befehl analysieren und ausfĂŒhren
    sf $43e                     * Floppy-VBL wieder erlauben
    lea stk(pc),a2              * Adresse des Stackpointerpuffers
    move.l (a2),d0              * Stackpointer holen
    bsr super                   * Supervisormodus aus (schaaade...)
    movem.l (sp)+,d0—d7/a0—a6   * Register wieder holen
    rts                         * und raus (ade!)

******************************
* wrfdc: Byte in d7 an den Controller
* schicken 
******************************
wrfdc:
    move.w #30,d1               * ZĂ€hler auf 30
    bsr as_time_goes_by         * Warteschleife
    move.w d7,daccess           * d7 ins Access-Register des DMA-Chips
    move.w #30,d1               * ZĂ€hler auf 30

******************************
* as_time_goes_by: Warteschleife
* mit d1 DurchlÀufen 
******************************
as_time_goes_by:
    dbf d1,as_time_goes_by      * Looping (huiii...)
    rts                         * back to the future

******************************
* warten_auf_godot : Wartet auf das IRQ-Signal
* des FDC 
******************************
warten_auf_godot:
    move.l #time,d7             * Konstante fĂŒr Timeout

poll:
    btst #5,mfp                 * IRQ am MFP7
    beq fix_und_fertig          * jawoll, Kommando ist ausgefĂŒhrt
    subq.l #1,d7                * TimeoutzÀhler magert ab
    beq game_over               * Sorry, zu lange gefackelt
    lea dflag(pc),a2            * Adresse des DMA-Flags
    tst.w (a2)                  * DMA aktiv?
    beq poll                    * nein, zum Polling
    lea buffer2(pc),a2          * Adresse der Adresse (!) des Reservepuffer!
    move.b dhigh,1(a2)          * Highbyte der DMA-Adresse
    move.b dmid,2(a2)           * Midbyte der DMA-Adresse
    move.b dlow,3(a2)           * Lowbyte der DMA-Adresse
    move.l buffer2(pc),d0       * DMA-Adresse nach dO
    cmp.l dend(pc),d0           * Mit Endadresse vergleichen
    blt poll                    * Noch nicht erreicht, weiter testen

    move.b #$d0,d7              * Force IRQ
    bsr irq                     * FDC unterbrechen
    lea dflag(pc),a2            * Adresse des DMA-Flags
    move.w #0,(a2)              * DMA beendet
    bra fix_und_fertig          * Feierabend

***************************
* Timeout-Konstante ist abgelaufen 
***************************
game_over:
    bsr fix_und_fertig          * Status lesen und in fst ablegen
    move.b #$d0,d7              * Force IRQ
    bsr irq                     * FDC unterbrechen
    lea timeout(pc),a2          * Adresse des Timeout-F1ags
    move.w #1,(a2)              * Timeout passiert
    lea dflag(pc).a2            * Adresse des DMA-Flags
    move.w #0,(a2)              * DMA völlig am Ende
    rts                         * und fertig

***************************
* DMA-Ubertragung oder FDC fertig 
***************************
fix_und_fertig:
    move.w daccess,d0           * Status lesen
    lea fst(pc),a2              * Adresse des FDC-Status-Flags
    move.w d0,(a2)              * Status ablegen
    rts                         * und raus

***************************
* status: Status und Bytezahl lesen 
***************************
status:
    move.w dmodus,d0            * Status lesen
    lea dst(pc),a2              * Adresse des DMA-Status-Flags
    move.w d0,(a2)              * Status schreiben
    clr.w d1                    * d1 löschen
    move.b dhigh,d1             * DMA-High
    lsl.l #8,d1                 * um ein Byte schieben
    move.b dmid,d1              * DMA-Mid
    lsl.l #8,d1                 * um ein Byte schieben
    move.b dlow,d1              * DMA-Low
    lea dend(pc),a2             * Adresse der Endadresse
    move.l d1,(a2)              * Endadresse
    sub.l dstart(pc),d1         * minus Start
    lea count(pc),a2            * Adresse des BytezÀhlers

****************************
* dma: dma setzen (Spiegelbild zu status) 
****************************
dma:
    lea dstart(pc),a2           * Adresse der Startadresse
    move.l d7,(a2)              * Startadresse ablegen
    move.b d7,dlow              * Lowbyte
    lsr.l #8,d7                 * um ein Byte schieben
    move.b d7,dmid              * Midbyte
    lsr.l #8,d7                 * um ein Byte schieben
    move.b d7,dhigh             * Highbyte
    move.l dstart(pc),d7        * Startadresse holen
    clr.l d0                    * d0 löschen
    move.w count(pc),d0         * Bytecounter nach dO
    add.l d0,d7                 * Addieren
    lea dend(pc),a2             * Adresse der Endadresse
    move.l d7,(a2)              * Endadresse ablegen
    rts

*****************************
* super: Schaltet vom Usermode
* in der Supervisormode und umgekehrt
* Übergabe des Stackpointers in d0 
*****************************
super:
    move.l d0,-(sp)             * Stackpointer auf Stack
    move.w #$20.-(sp)           * SUPER
    trap #1                     * im GEMDOS
    addq.l #6,sp                * Stack korrigieren
    rts

*****************************
* analyze: Analysiert grob, welche Art von
* Befehl vorliegt
* und verteilt entsprechend
* in: d6.w Opcode 
***************************** 
analyze:
    move.w d6,d7                * Opcode retten
    move.w d6,d5                * gleich nochmal
    btst #7,d6                  * Bit 7 testen
    bne typii                   * kein Typ-I-Befehl
    and.b #$f0,d6               * obere 4 Bits ausmaskieren
    cmp.b #16,d6                * SEEK-Befehl?
    beq such_hasso              * jawoll
    bne step_dance              * kein Seek. aber Typ-I

typii:
    btst #6,d6                  * Bit 6 testen
    bne typiii                  * kein Typ-II-Befehl
    btst #5,d6                  * Bit 5 testen
    beq rdsector                * gelöscht, also Read-Sector-Befehl
    bne wrsector                * Write-Sector-Befehl

typiii :
    and.b #$f0,d6               * obere 4 Bits ausmaskieren
    cmp.b #$c0,d6               * Read Adress?
    beq rdadr                   * jawoll
    cmp.b #$e0,d6               * Read Track?
    beq rdtrk                   * jawoll

*                               * alles andere ist Force Interrupt

**************************
* irq: Unterbricht den Controller 
**************************
irq:
    bsr wrfdc                   * d7 an den Controller
    move.w #250,d1              * 250 SchleifendurchlÀufe
    bra as_time_goes_by         * kurz warten

*****************************
* rdsector: Liest einen Sektor oder
* gleich einen ganzen Haufen davon 
*****************************
rdsector:
    move.l buffer1(pc),d7       * Pufferadresse
    bsr dma                     * als DMA-Adresse
    lea dflag(pc),a2            * Adresse des DMA-Flags
    move.w #1,(a2)              * DMA-Flag setzen
    move.w #$90,dmodus          * mit der Sehreib/Leseleitung
    move.w #$190,dmodus         * kippeln, löscht den DMA-Status
    move.w #$90,dmodus          * Sektorzahler des DMA-Chips
    move.w #14,d7               * maximal 14 Sektoren (utopisch)
    bsr wrfdc                   * d7 an FDC
    move.w #$84,dmodus          * Sektorregister
    move.w sector(pc),d7        * Sektornummer
    bsr wrfdc                   * d7 an FDC
    move.w #$80,dmodus          * Kommandoregister
exe:
    move.w d5,d7                * Kommando von d5 nach d7
    bsr wrfdc                   * d7 an FDC
    bsr warten_auf_godot        * Ende des Kommandos abwarten
    bra status                  * Status lesen

*****************************
* rdtrk: Track lesen 
*****************************
rdtrk:
    move.l buffer1(pc),d7       * Adresse des Spurpuffers
    bsr dma                     * DMA initialisieren
    lea dflag(pc),a2            * Adresse des DMA-Flags
    move.w #1,(a2)              * DMA in Arbeit!
    move.w #$90,dmodus          * Schreib/Leseleitung
    move.w #$190,dmodus         * umschalten, siehe oben
    move.w #$90,dmodus          * SektorzÀhler des DMA-Chips
    move.w #14.d7               * 14 Sektoren
    bsr wrfdc                   * d7 an FDC
    move.w #$80,dmodus          * Kommandoregister
    bra exe                     * Kommando schreiben und beenden

*****************************
* wrtrk: Track schreiben 
*****************************
wrtrk:
    move.l buffer1(pc),d7       * Spurpufferadresse nach d7
    bsr dma                     * DMA initialisieren
    lea dflag(pc),a2            * Adresse des DMA-Flags
    move.w #1,(a2)              * DMA in Arbeit!
    move.w #$190,dmodus         * Schreib/Leseleitung umschalten
    move.w #$90,dmodus          * (bewirkt was? Raten Sie mal!)
    bsr wrfdc                   * d7 an FDC
    move.w #$100,dmodus         * Kommandoregister wÀhlen
    bra exe                     * Kommando schicken und beenden

*****************************
* wrsector: Sektor schreiben 
***************************.1*
wrsector:
    move.l buffer1(pc),d7       * Adresse des Puffers
    bsr dma                     * DMA initialisieren
    lea dflag(pc).a2            * Adresse des DMA-Flags
    move.w #1,(a2)              * DMA in Arbeit
    move.w #$190.dmodus         * mit der Schreib/Leseleitung
    move.w #$90,dmodus          * klappern
    move.w #$190,dmodus         * SektorzÀhler des DMA-Chips
    move.w #14,d7               * Maximal 14 Sektoren (utopisch)
    bsr wrfdc                   * d7 an FDC
    move.w #$184,dmodus         * Sektorregister des FDC
    move.w sector(pc),d7        * Startsektor holen
    bsr wrfdc                   * d7 an FDC
    move.w #$180,dmodus         * Kommandoregister des FDC
    bra exe                     * Kommando schicken und beenden

*****************************
* step_dance: einheitliche Routine fĂŒr Step,
* step-in, step-out, restore 
*****************************
step_dance:
    move.w #$80,dmodus          * Command-Register auswÀhlen
    bsr wrfdc                   * Byte in d7 an FDC
    bra warten_auf_godot        * Auf FDC warten...

*****************************
* such_hasso: Seek-Befehl 
*****************************
such_hasso:
    move.w #$86,dmodus          * Datenregister auswÀhlen
    move.w track(pc).d7         * Tracknummer holen
    bsr wrfdc                   * Byte in d7 an FDC
    move.w #$80,dmodus          * Commandregister
    move.w d5,d7                * Kommando
    bsr wrfdc                   * Byte an FDC
    bra warten_auf_godot        * Auf FDC warten

*****************************
* rdadr: Adreßfeld lesen 
*****************************
rdadr:
    move.l buffer1(pc),d7       * Adresse des Puffers holen
    move.l buffer2(pc),a3       * Adresse des Reservepuffers
    bsr dma                     * DMA initialisieren
    move.w #$90,dmodus          * Schreib/Leseleitung
    move.w #$190.dmodus         * umschalten
    move.w #$90,dmodus          * und auf SektorzÀhler
    move.w #1.d7                * Maximal 1 Sektor
    bsr wrfdc                   * d7 an FDC (DMA)
    move.w #$80,dmodus          * Kommandoregister
    move.w count(pc),d2         * Wieviele ID-Felder?

nochnid:
    move.w d5,d7                * Kommando holen
    bsr wrfdc                   * und schreiben
    bsr warten_auf_godot        * auf FDC warten
    move.w fst(pc),d1           * FDC-Status holen
    move.w d1,(a3)+             * Status in den zweiten Puffer
    dbra d2,nochnid             * Noch eine ID
    bra status                  * und zum Status

SELECT.S


****************************
* SELECT.S
* Selektiert Seite und Laufwerk
* Mit Schnittstelle fĂŒr BASIC
* (C) 1987 and for all eternity by
* Claus Brod
* Am Felsenkeller 2
* 8772 Marktheidenfeld
* Version 1.0
* Last update 16.6.87
* Assembliert mit AS68 
*****************************

*****************************
* Definitionen 
*****************************
snd = $ff8800                   * Adresse des Soundchips
sndwrt = $ff8802                * Ein/Ausgabe des Soundchips
dmodus = $ff8606                * DMA-Modus-Register
daccess = $ff8604               * DMA-Access

*****************************
* Sprung ins Hauptprogramm 
*****************************
    bra main 
*****************************
* Eingabefeld fĂŒr BASIC 
*****************************
laufwerk: .dc.w 0               * Eingabeparameter Laufwerk und Seite
parm: .dc.l 0                   * Reserveein/ausgabefeld fĂŒr Erweiterungen
temp: .dc.l 0                   * Reservefeld
stk:  .dc.l 0                   * Puffer fĂŒr Stackpointer

*****************************
* Und jetzt das Hauptprogramm 
*****************************
main:
    movem.l d0—d7/a0—a6,—(sp)   * Register retten
    clr.l d0                    * Userstack wird Supervisorstack
    bsr super                   * Supervisormode an
    lea stk(pc),a2              * Stackpointer
    move.l d0,(a2)              * retten

    st $43e                     * Floppy—VBL sperren
    move.w laufwerk(pc),d7      * Laufwerksnummer holen
    bne mach_mal                * Wenn nicht gerade 0, dann mach los
    move.w #$80,dmodus          * Statusregister
motor:
    move.w daccess,d1           * auslesen
    btst #7,d1                  * Motor noch an?
    bne motor                   * jawoll
mach_ma1:
    cmpi.b #8,d7                * Laufwerkscode grĂ¶ĂŸer als 7?
    bge extend                  * ja, extended mode
    eor.b #7,d7                 * Bits invertieren
    and.b #7,d7                 * und ausmaskieren
    move.w sr,-(sp)             * Status retten
    or.w #$700,sr               * Interrupts aus
    move.b #14,snd              * Port A wÀhlen
    move.b snd,d0               * Port A lesen
    and.b #$f8,d0               * ausmaskieren
    or.b d0,d7                  * neue Seite/neues Laufwerk setzen
    move.b d7,sndwrt            * in Port A
    sf $43e                     * Floppy-VBL erlauben
    move.w (sp)+,sr             * Status wieder holen
raushier:
    lea stk(pc),a2              * Stackpointer
    move.l (a2),d0              * zurĂŒckholen
    bsr super                   * wieder in den Usermode
    movem.l (sp)+,d0-d7/a0-a6   * Register zurĂŒckholen
    rts                         * und raus

*************************
* super: Schaltet vom Usermode
* in den Supervisormode und umgekehrt
* in: d0 Stackpointer 
*************************
super:
    move.l d0,-(sp)             * Stackpointer auf Stack
    move.w #$20,-(sp)           * SUPER
    trap #1                     * im GEMDOS
    addq.l #6,sp                * Stack reparieren
    rts                         * und raus

**************************
* time: Warteschleife 
**************************
time:
    dbra d1,time                * ZĂ€hler magert ab
    rts                         * und raus

**************************
* msg: Gibt String ab a2 aus 
**************************
msg:
    move.l a2,-(sp)             * Adresse des Strings auf den Stack
    move.w #9,-(sp)             * PRINT LINE
    trap #1                     * im GEMDOS
    addq.l #6,sp                * Stack korrigieren
    rts                         * und raus

**************************
* extend: Wird in der nÀchsten ST ergÀnzt 
**************************
extend:
    lea nochnit(pc),a2          * extend noch nicht
    bsr msg                     * implementiert
    bra raushier                * und raus

nochnit:
.dc.b 'Routine noch nicht implementiert.',13,10,0