Floppy-Spielereien Teil 3: Durch dunkle Kanäle

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

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

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

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!

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


Aus: ST-Computer 09 / 1987, Seite 92

Links

Copyright-Bestimmungen: siehe Über diese Seite