Willkommen, bienvenue, welcome - kommen Sie, staunen Sie! Erleben Sie die Enthüllung der letzten Rätsel der Floppyprogrammierung! Delektieren Sie sich an den exotischsten Aufzeichnungsverfahren und freuen Sie sich mit mir an den (blauen) Wundern der Technik... treten Sie doch ein...
Bisher habe ich im Floppykurs immer einigermaßen elegant um das im Untertitel erwähnte Nahrungsmittel herum geredet, ohne auf den ganz entscheidenden Punkt zu kommen: Den Floppycontroller. Doch zu meiner Verteidigung muß ich sagen, daß erstens dieses Kapitel wahrscheinlich das komplizierteste ist und deswegen wohl vorbereitet werden mußte; zum Zweiten hat sich einfach im Laufe des Kurses einfach herausgestellt, daß auch die anderen Themen einer breiteren Darstellung bedürfen, damit man sie auch bleibend versteht.
Doch zuguterletzt können wir uns auch diesen Brocken vornehmen. Zuguterletzt, Sie haben richtig gelesen. Ich möchte nach dieser Folge zumindest eine kleine Pause machen, um wieder abseits vom Schreiberischen kreativ zu werden (wer hat da so hämisch gelacht ????). Ob ich die Serie fortsetze, hängt nicht zuletzt von Ihnen ab (schauen Sie sich nicht um, ob jemand anders gemeint sein könnte -ich meine SIE!), genauer: von Ihrer Reaktion und Meinung, telefonisch oder schriftlich bei mir oder bei der ST kundgetan. Zum Zeitpunkt, an dem just diese Zeilen zäh aus meinen Fingern fließen (das war eine krumme Metapher und keine Beschreibung einer anatomischen Absonderlichkeit), habe ich zwar (ohne Schummeln!) durchweg positive Kritiken gehört, aber vielleicht hat sich einfach keiner zu stänkern getraut oder mittlerweile, wenn dieser Teil zu Ihnen gelangt, haben Sie die Nase voll von mir.
Aber wenn ich noch länger herumlabere, kann ich harscher Kritik gewiß sein. Also: Ran an den Speck.
Zuerst ein Wort von Kommissar Pingelig, der in den letzten beiden Folgen des Floppykurses ein paar Fehler gefunden hat:
Die Korrektur (betreffend das Listing von HYPERFORMAT) in der ST 7/87, Seite 72, ist selbst korrekturbedürftig. Zum Beispiel muß es in der Routine change_tos ”move.b = //xx,BF(a5)” heißen statt ”move.b #xx,$6F(a5)”. ”xx” steht für 21 in der „Alt“-Spalte bzw. 19 in der „Neu“-Spalte. Zudem habe ich eine noch günstigere Lückenbyte-Kombination gefunden, die in den neuesten HYPERFORMAT-Versionen eingebaut ist. Sie lautet 1-2-21-11-2 (und nicht wie im Listing 3-3-21-11-3 bzw. in der Korrektur 1-1-19-9-7). Zudem fehlten in der Korrektur ein paar Zeilen:
Für die neuen Leser fasse ich zusammen, was wir aus den vorherigen Folgen über den Floppycontroller wissen. Vor allem im dritten Teil haben Sie dazu ja schon einiges erfahren. Der Floppycontroller (im folgenden nur noch FDC für Floppy Disk Controller, meine Fir yer laufen sonst heiß) bekommt übei den Umweg des DMA-Controllers Daten vom Prozessor, die er doch bitteschön auf die Diskette schreiben soll. Der Controller kümmert sich selbständig darum, daß die Daten an den richtigen Platz auf einer Spur geschrieben werden (also in den richtigen Sektor) und daß sie ordentlich kodiert werden. Umgekehrt liefert der Controller beim Lesen dem Prozessor dekodierte Daten von der Diskette. Der FDC ist also der Schreiberling des Systems.
Kodieren? Dekodieren? Könnte man ein gesetztes Bit nicht einfach als Magnetisierung in die eine Richtung und ein Nullbit als anders gerichtete Magnetisierung schreiben? Naja, wenn ich schon so frage...
In Wirklichkeit kommt bei der Aufzeichnung eine weitere Gemeinheit ins Spiel: Die Daten werden kodiert auf die Diskette geschrieben, damit man den Lesevorgang synchronisieren kann.
Wie der Floppycontroller auf der Diskette den Anfang eines Bytes findet, habe ich ja bereits in der letzten Folge zu erläutern versucht. Worum es jetzt geht: Wie finde ich den Anfang eines Bits? Nehmen wir mal an, die Bits würden so auf die Diskette geschrieben wie oben erwähnt. Ein $A1-Byte (dezimal 161, binär 1010 0001) würde dann als Signalpegelverlauf so aussehen (Bild 1):
Ich hoffe,. das Schema ist auch dem Nicht-Elektroniker klar: Der durchgezogene dicke Strich soll den Verlauf der Aufzeichnung darstellen. Ist der Strich oben (in unserem ersten Beispiel also dann, wenn das zu schreibende Bit gesetzt ist), wird das Stück Plastikscheibe unter dem Lesekopf in die eine Richtung (nennen wir’s mal „rechts“) magnetisiert. Wenn nicht, wird „links“ magnetisiert. Mit Bitzelle bezeichne ich im folgenden den Abschnitt zwischen zwei gestrichelten senkrechten Linien.
Dieses erste Verfahren in Bild 1 heißt NRZ (Non-Return-To-Zero), nicht weil es etwa nie auf den Signalpegel 0 gehen würde, sondern weil es nicht in der Mitte einer Bitzelle auf 0 wechselt, sondern höchstens mal am Rand - zu solchen Kodierungen kommen wir später. NRZ zeichnet sich durch Einfachheit und Unbrauchbarkeit aus. Daß das NRZ einfach ist, sehen Sie schon daran, daß Sie meine weit ausholenden Erläuterungen dazu recht unnötig finden - schließlich liegt das Prinzip auf der Hand. Und unbrauchbar ist es, weil ein Floppycontroller sich an so einem Signal auf der Diskette nicht synchronisieren kann. So sauber wie oben dargestellt sieht eine Aufzeichnung nämlich nie aus - es gibt immer wieder Unregelmäßigkeiten im Material, in der Drehgeschwindigkeit, in der Elektronik des Lesekopfes usw. . Das bewirkt, daß einzelne Abschnitte des obigen Linienzuges gestaucht oder gedehnt werden. Stellen Sie sich vor, das passiert während der vier Null-Bits im Bild 1. Dann läßt sich nicht mehr mit Sicherheit sagen, ob tatsächlich vier oder vielleicht drei oder fünf Null-Bits auf der Diskette stehen (entsprechende Probleme gibt es bei längeren Sequenzen von 1-Bits).
Man bräuchte also ein Aufzeichnungsverfahren, bei dem einem der Takt, in dem die Bits auf die Scheibe gekleistert wurden, förmlich in die Äuglein springt. Voil (Bild 2):
Beim Manchester- oder Bi-Phase-Mark-Verfahren wird am Anfang einer Bitzelle grundsätzlich der Zustand gewechselt - entweder von „0“ (= „links“ magnetisiert) auf „1“ ( = „rechts“ magnetisiert) oder umgekehrt, je nachdem, was vorher übertragen wurde. Dadurch erreicht man, daß regelmäßig Pegelwechsel beim Lesen auftreten, an denen sich der Controller orientieren kann. Das eigentliche Datenbit wird in der Mitte der Bitzelle übertragen. Wenn das Datenbit gesetzt ist, wird in der „Bitmitte“ ein zusätzlicher Übergang von „0“ auf „1“ oder umgekehrt erzeugt. Dieses Verfahren ist schon ziemlich zuverlässig, weil keine längeren konstanten Pegel auftreten, denen man keine Information über den Takt der Aufzeichnung (also über die tatsächlichen Längen der einzelnen Bits auf der Diskette) entlocken kann.
Eine ähnliche Technik wurde jahrelang auf den meisten Disketten angewendet: Die FM-Technik (Frequency Modulation). Dabei wird grundsätzlich in der ersten Hälfte der Bitzelle eine „1“ erzeugt und in der zweiten Hälfte das eigentliche Datenbit übertragen (Bild 3):
FM- wie Manchesterkode haben den entschiedenen Nachteil, daß sehr viele Übergänge übermittelt werden müssen. Damit man die auch einigermaßen beim Lesen erkennen kann, muß man sich relativ viel Platz auf der Diskette lassen. NRZ- und FM-Kode haben einen weiteren Nachteil gemeinsam: Um einen Datenwert zu erkennen, muß man einen Zustand lesen (beim NRZ-Verfahren eine ganze Bitzelle lang, beim FM-Verfahren in der zweiten Hälfte einer Bitzelle). Wer etwas vom Wesen der magnetischen Induktion weiß, wird mir bestätigen, daß es technisch viel einfacher wäre, wenn der zu übertragende Wert des Bits durch eine Flanke gekennzeichnet wäre, wie beim Manchester-Verfahren. Wobei anzumerken ist, daß Flanke hier nichts mit Manni Kaltz und schon gar nichts mit Bayern München zu tun hat; Flanke bedeutet hier einen Wechsel von „0“ auf „1“ oder umgekehrt.
Also hat man sich Kodes gesucht, die alles auf einmal bieten: Datensicherheit durch einkodierten Takt, hohe Aufzeichnungsdichte durch seltene Wechsel und einfache Lesbarkeit durch Flankenkodierung. Und man hat sie gefunden (Bild 4).
Wie man sieht (oder auch nicht), ist dieser MFM-Kode (ich vermute: Modified FM-Kode ) eher eine Abart des Manchester-Kodes. Wie beim Letzteren wird bei einem gesetzten Datenbit in der Bitmitte der Zustand gewechselt (was ist das also? Richtig: Eine Flanke!); die Flanken am Anfang der Bits (für den Takt) fallen weg - die sind ja auch bei 1-Bits nicht nötig, weil da eh schon eine Flanke anfällt und man somit leicht den Takt erspüren kann. Bei mehreren O-Bits hintereinander wird die Sache aber kritisch. Deswegen schiebt man ab der zweiten aufeinanderfolgenden Null am Anfang der Bitzelle einen Extraübergang ein.
Bei MFM braucht man nur noch wenig mehr Pegelwechsel als bei NRZ (und damit auch viel weniger Platz als bei FM). Deswegen hat sich dieses Format durchgesetzt.
Nachdem Sie sich durch alle fiesen Details gekämpft haben, muß ich Sie ins kalte Wasser stoßen: Auf dem ST werden Sie dieses neue Wissen so schnell nicht anwenden können. Denn der Controller, der Baustein im ST, den wir im folgenden kennenlernen, macht die MFM-Kodierung ganz alleine - der Programmierer hat darauf keinen Einfluß. Diese Einführung in die Kodierungsverfahren auf magnetischen Datenträgern sollte vielmehr ein Gefühl vermitteln, was der Floppycontroller tatsächlich alles leistet. In den Pionierrechnern erledigte diese Arbeit der Prozessor! Beim Brotkasten C64 behalf man sich mit einer intelligenten Floppystation, der 1541, in der ein eigener 6502-Prozessor und diverse andere Chips die Fronarbeit auf der Diskette verrichteten. Beim AMIGA ist man wieder zur alten Linie zurückgekehrt: Hier teilen sich Blitter, DMA-Chip, Portia (der Sound- und I/O-Chip) und der Prozessor die Arbeit, um auch ohne einen „richtigen“ Floppycontroller einigermaßen zu Rande zu kommen.
Trotzdem ist das Wissen um den MFM-Kode nicht vergebens. Auf dem AMIGA, zum Beispiel, hat man eben durch seine recht eigenwillige Konstruktion wesentlich mehr Einfluß auf die Datenaufzeichnung als beim ST. Mit dem AMIGA müßte es möglich sein, auch andere Kodierungsverfahren anzuwenden. Ebenso bei der guten alten 1541 von Commodore, die nach meiner Einschätzung flexibel genug sein müßte, um sogar MFM zu lesen - ab Fabrik kann sie allerdings nur den Commodore-typischen „GCR“-Kode lesen (damit will ich Sie aber nicht auch noch quälen).
Ebenso dürfte es wohl einige flexible Controller in der PC-Welt geben. Wer also nebenher noch einen anderen Rechner besitzt, kann sich ja mal kundig machen - und bald gibt es dann überall Diskkonverterprogramme für IBM nach Commodore, AMIGA nach ST und umgekehrt, APPLE-CP/M nach ST-CP/M und so fort - wär das nichts? Damit wäre endlich der kundenfeindliche Spezialitätenfetischismus bei den Computerfirmen überwunden! Und schließlich gibt es ja schon Erweiterungskarten für den ST, an die man die gesamte PC-Peripherie hängen kann. Und da gibt es ja bekanntlich alles, auch flexible Controller...
Nur das Macintosh-Format ist praktisch auf Diskettenebene nicht zu konvertieren. Der Macintosh verwendet auf den einzelnen Tracks unterschiedliche Drehzahlen, um mehr auf die Diskette unterzubringen (schafft aber auch nicht mehr Bytes als ein ST oder AMIGA). Die meisten Laufwerke drehen aber (glücklicherweise) ziemlich konstant und lassen sich davon auch durch heftigste Trickserei nicht abbringen. Deswegen ist man bei den Mac-Emulatoren für den ST auch immer auf die Krücke mit dem RS232-Kabel angewiesen. Aber nun zum Thema des Tages:
Der WD1772 (so heißt er nämlich) ist eigentlich ein eigener kleiner Prozessor. Deswegen hat er auch interne Register, von denen fünf 8-Bit-Register dem Programmierer zur Verfügung stehen: Statusregister, Trackregister, Sektorregister, Datenregister und Kommandoregister. Track- und Sektorregister sollten intuitiv klar sein. Hier speichert der Controller den aktuellen Track und Sektor, hier kann man ihm aber auch das Ziel einer Operation mitteilen. Das Datenregister enthält die gerade zu lesenden/schreibenden Bytes sowie bei manchen Befehlen Parameter. In das Kommandoregister kann man nur schreiben, und zwar Befehle (was auch sonst). Das Statusregister kann man nur lesen. Der Status ist wie folgt in die einzelnen Bits kodiert (Tabelle 1):
Nun zur Übersicht der Controllerbefehle. Sie werden in das Kommandoregister geschrieben (logo!), dazu braucht der Controller von Fall zu Fall Parameter in anderen Registern.
Sie dienen dazu, den Lesekopf des Laufwerks in die richtige Position zu bringen. Hier eine kurze Zusammenfassung:
RESTORE (in Bits: 0 0 0 0 h V rl rO):
positioniert den Lesekopf auf die Spur 0. Die Buchstaben im Kommandowort bedeuten folgendes:
„h“ ist das Motor-On-Flag. Ist es gelöscht, testet der Controller, ob der Motor vielleicht noch vom vorigen Kommando läuft. Wenn nein, fährt er ihn hoch, wenn ja, wird der Befehl gleich ausgeführt. Ist das h-Bit gesetzt, fährt der Controller auf jeden Fall den Motor hoch.
„V“ ist das Verify-Flag. Nachdem der Befehl ausgeführt wurde, vergleicht der Controller die Spurnummer im Trackregister mit der Tracknummer, die er im nächsten Vorspannfeld auf der Diskette findet - wenn „V“ gesetzt ist. Klappt das Verify nicht, wird das im RECORD NOT FOUND-Bit gemeldet.
„r1“ und „r0“ geben zusammen an, wie schnell der Controller den Track wechseln soll (abhängig von der Güte des Laufwerks). „00“ (also r1=0 und r0 = 0) bedeutet 2 Millisekunden, „01“ (r1=0, r0=1) 3 ms, „10“ 5ms und „11“ 6ms. Die Laufwerke von ATARI sind für eine Steprate von drei Millisekunden geeignet.
Kurz noch zur Kodierung (für die Nicht-Assembleristen unter Ihnen) ein Beispiel: Das Befehlsbyte 0 bedeutet, daß ein Restore-Befehl ohne jede Raffinessen und mit Steprate 2ms ausgeführt werden soll. 5 (binär 00000101) bedeutet einen Restorebefehl mit Verify und Steprate 3 ms.
SEEK (in Bits: 0 0 0 1 h V r1 r0): Fährt einen bestimmten Track an. Dazu übergibt man vor dem Kommando den gesuchten Track ins Datenregister.
STEP-IN (in Bits: 0 1 0 u h V r1 r0): Bewegt den Lesekopf einen Track nach innen (also zur Mitte der Diskette hin, zu höheren Tracknummern). Das „u“-Bit bewirkt, wenn es gesetzt ist, daß das Spurregister auf dem Laufenden gehalten wird („update-Flag“).
STEP-OUT (in Bits: 0 1 1 u h V r1 r0): Raten Sie mal!
STEP (in Bits: 0 0 1 u h V r1 r0): Bewegt den Lesekopf einen Track weit in die Richtung des letzten Step-Befehls.
Tabelle 1: Das Statusregister des Floppycontrollers)
READ-SECTOR (1 0 0 m h E 0 0): Vor dem Kommando wird dem Controller die Sektornummer im Sektorregister übergeben. Er liest dann den gewünschten Sektor auf der aktuellen Spur.
Das „E“-Bit gibt dem Lesekopf 30 m; Zeit, um sich auf die Diskette abzusenken. Allerdings liegt der Lesekopf der ST-Laufwerke immer auf, so daß da? Bit eigentlich unnötig ist (auf 0 setzen!). Die Entwicklungsdokumentation von ATARI warnt sogar eindringlich davor, dieses Bit zu setzen (warum auch immer).
Das „m“-Bit (m wie multiple) gibt an, ob gleich mehrere Sektoren gelesen (oder beim Write-Sector-Befehl geschrieben) werden sollen. Dazu lädt man die Nummer des ersten Sektors in das Sektorregister, und der Controller liest/ schreibt dann alle folgenden Sektoren, bis er keinen mehr findet oder herb unterbrochen wird (dazu später).
WRITE-SECTOR (1 0 1 m h E P a0): Sektornummer ins Sektorregister, Befehl ins Kommandoregister, und schon rasselt er los: WRITE-SECTOR prüft erst mal nach, ob die Diskette schreibgeschützt ist; wenn ja, läuft gar nichts (wie bei allen Schreibbefehlen). Der Befehl sucht nach dem passenden Sektorvorspann und schreibt dann nacheinander Daten-Adreßmarke, Sektordaten und die Checksumme über die Sektordaten.
„P“ gibt an, ob die Daten vorkomprimiert werden sollen (Precompensation-Bit). Ist das Bit = 0, wird komprimiert, wenn nicht, dann nicht. Auf ATARI-Disketten wird immer komprimiert, wegen der höheren Datensicherheit.
„aO“ = 0: normale Daten-Adreßmarke schreiben (SFB), a0=l: „gelöschte“ Daten-Adreßmarke ($F8) schreiben. Dient zur Unterscheidung von Sektoren. Hier könnte auch ein (primitiver) Kopierschutz ansetzen.
READ-ADDRESS (1 1 0 0 h E 0 0): Liest ein Vorspannfeld ein, das dann so aussieht:
Auf der Diskette wird ein Vorspann durch Syncbytes und durch die ID-Adreßmarke (SFE) angekündigt (siehe Floppykurs Teil III). Die Spurnummer wird auch ins Sektorregister geladen.
READ-TRACK (1 1 1 0 h E 0 0): Liest eine ganze Spur ein. Dieser Befehl ist aber sehr unzuverlässig. Verwenden kann man ihn eigentlich nur, um Abstände zwischen Daten auf der Diskette zu messen. Die Daten selbst sind mit Vorsicht zu genießen. Beim Lesen einer ganzen Spur erkennt der Controller nämlich viel zu oft imaginäre Synchronisationsbytes (er ist halt ein Sensibelchen) und verfälscht darauf folgende Bytes. Erst beim nächsten tatsächlich vorhandenen Syncbyte erkennt er den Fehler. Nach dem zweiten gelesenen real existierenden Synchronisationsbyte hat sich der FDC gefangen und liest ein paar korrekte Bytes. Das bedeutet, daß mindestens das dritte Synchronisationsbytes richtig gelesen wird. Daran kann man sich zumindest orientieren.
WRITE-TRACK (1 1 1 1 h E P 0): Eine Spur schreiben, also formatieren. Man übergibt dem Controller einen ganzen Track voll Daten und Bruchteile von Sekunden hat man etwas völlig anderes auf der Spur stehen. Das liegt daran, daß alle Bytes von $F5 bis $FF als Steuerbytes interpretiert werden. Was sie bewirken? Das hier (Tabelle 2):
Ein paar Takte zur Prüfsummenbildung: Sie geschieht automatisch im Controller selbst. Wenn man dem Controller beim Schreiben einer Spur ein Syncbyte $A1 übergibt, löscht er intern die Checksumme und fängt neu mit ihrer Berechnung an. Jedes jetzt ankommende Byte wird durch eine Hardwaremühle gedreht (das Verfahren nennt sich CRC, Cyclic Redun-dancy Check), die eine Prüfsumme erzeugt. Sendet man ein $F7-Byte, wird diese Checksumme 16 Bit breit auf die Diskette geschrieben (für diesen Zweck hat der FDC übrigens ein internes Register, das 16 Bit breit ist). Auch der Write-Sector-Befehl erstellt eine solche Prüfsumme. Beim Lesen funktioniert das Ganze dann entsprechend anders herum.
Ein Sonderling unter den Controllerbefehlen ist...
FORCE INTERRUPT (1 1 0 1 I3 I2 I1 I0): Löst eine Unterbrechung des aktuellen Controllerbefehls aus. I0 und I1 tun nichts zur Sache, sie müssen 0 sein. Wenn 12 gesetzt ist, wird bei jeder Umdrehung ein Interrupt ausgelöst (damit kann man die Umdrehungsgeschwindigkeit messen). Wenn I3 gesetzt ist, wird das laufende Kommando mit einer Interruptanforderung des Controllers beendet. Sind I2 und I3 gleich 0, wird das laufende Kommando ohne Interrupt abgewürgt. Puh, das war’s. Sie sind noch dabei? Dann darf ich Ihnen zu Ihrem Durchhaltevermögen gratulieren. Wie Sie die Befehle anwenden, können Sie aus dem Listing LOCKSLEY.S in der vorigen Folge des Floppykurses erfahren. Für diejenigen, die diesen legendären Artikel nicht gelesen haben, ein kurzes Anwendungsbeispiel (Bild 5).
* Floppyroutine für den Step-Befehl
* (Beispiel)
* muß im Supervisormodus laufen, der Floppy-VBL muß abgewürgt
* werden (z. B. mit st $43e)
daccess = $FFFF8604
dmodus = $FFFF8606
mfp = $FFFFFA01
move.w #%00110001,d7 * Step-Befehl mit Track-Update, Steprate 3
move.w #$180,dmodus * Kommandoregister auswählen
* (siehe Teil III des Floppykurses)
bsr wrfdc * d7 an DMA bzw. FDC schicken
move.l #$40000, d7 * Timeout-Konstante
schau_nach:
btst #5,mfp * Fertigmeldung des FDC am MFP68901?
beq fertig * Jawohl, Kommando ist ausgeführt
subq.l #1,d7 * Timeoutzähler dekrementieren
bne schau_nach * Noch nicht auf 0, dann weiter warten
move.b #$d0,d7 * FORCE-IRQ-Befehl
bsr wrfdc * d7 an DMA bzw. FDC schicken
move.w #250,d1 * Zähler auf 250
bsr wait * IRQ-Befehl Zeit lassen
fertig:
rts * und Schluß
move.w #30,d1 * Zähler auf 30
bsr wait * Kurze Warteschleife (30 Durchläufe)
move.w d7,daccess * Befehl an DMA bzw. FDC schicken
move.w #30,d1 * Zähler auf 30
bsr wait * Warteschleife (wie oben)
rts * und zurück
Bild 5: Steptanz mit dem Lesekopf
Diese Routine ist nicht ganz bis zum bitteren Ende ausprogrammiert, das Drumherum (vor allem die Zeitschleife, das Einschalten des Supervisormodus et cetera) finden Sie vollständig im Listing zu LOCKSLEY.S (Teil III des Floppykurses).
Zu Beginn wird das DMA-Modus-Register so eingestellt, daß das Kommandoregister des FDC offen vor uns im DACCESS-Register vor uns liegt. Dort plazieren wir den STEP-Befehl selbst (mit bsr wrfdc). Die WRFDC-Routine beinhaltet auch noch zwei vorsichtige Warteschleifen (besser Vorbeugen als ärgern) vor und nach dem Beschreiben des DACCESS-Registers, vor allem, um dem FDC Zeit zu lassen, seine Fertigmeldung vom vorangegangenen Befehl zurückzuziehen. Im folgenden fragen wir nämlich in einer Schleife genau diese Fertigmeldung (im Bit 5 des ersten Registers des MFP-68901) ab. Fragen wir zu fix nach dem Kommando auf 'Fertig’ ab, hat der FDC eventuell noch gar nicht realisiert, daß da ein Kommando angekommen ist, und tut so, als wäre er schon fertig. Die Warteschleife wird abgebrochen, wenn die Fertigmeldung innerhalb einer bestimmten Zeit ankommt, die durch die Timeout-Konstante $40000 gegeben ist. Ansonsten wird das laufende Kommando brutal abgewürgt (mit einem FORCE-IRQ-Befehl) und die Routine verlassen.
Mit dem EXTENDED MINIMON und LOCKSLEY.S aus den vorherigen Folgen können Sie (in den Grenzen des Controllers) völlig beliebig formatierte Disketten erzeugen. Zum Beispiel können Sie im Vorspannfeld jedes Sektors für die Größe des nachfolgenden Sektors nicht nur die Zahlen 0-3 verwenden, sondern auch höhere. Allerdings werden von den höheren Zahlen auch wieder nur die untersten beiden Bits beachtet, das heißt, eine 4 entspricht einer 0 (also einer Sektorgröße von 128 Bytes). Das kann man für einen Kopierschutz verwenden:
Schließlich bin ich Ihnen noch eine Ergänzung des EXTENDED MINIMON aus der letzten Folge schuldig. Mit dieser Erweiterung bekommen Sie die Möglichkeit, von BASIC aus sogar die einzelnen Register von Floppycontroller und DMA-Chip zu lesen und zu modifizieren. Sie finden die dazu notwendigen MINIMON-Erweiterungen in den Listings 1 und 2. Im Quellkode von SELECT.S, der Selektierroutine des MINIMON, hatte ich dafür mit Bedacht eine Abfrage auf erweiterte Steuerkodes vorgesehen. Sie müssen also nur die Routine extend ein bißchen abändern und die neu hinzugekommenen Routinen eintippen. Für BASIC-Fans gibt es in Listing 2 eine Datawüste des gesamten SELECT.S, mit denen Sie die alten SELECT-Datazeilen im EXTENDED MINIMON ersetzen können. Zusätzlich ist im Listing 2 auch eine kleine BASIC-Routine (für den EXTENDED MINIMON) abgedruckt, mit der Sie die einzelnen Register lesen und beschreiben können.
Wie steuert man diese Erweiterung an? Normalerweise übergibt man der SELECT-Routine in der Variablen Laufwerk (eigentlich ist das eine relativ feste Speicherstelle, im Basicprogramm sel+3) Zahlen von 0 bis 7, die Seite und Nummer des Laufwerks angeben, das angewählt werden soll. Übergibt man Zahlen, die größer als 7 sind, spricht die Abfrage auf „extended commands“ an. „8“ bedeutet, daß Register des Floppycontrollers gelesen werden sollen, „9“, daß Register geschrieben werden. Mit „10“ kann man das DMA-Statusregister lesen.
Bei den Leseroutinen bekommt man generell den Inhalt des gewünschten Registers in temp (sel+ 8 für die BASIC-Fans) zurück. Das gewünschte Register übergibt man in parm (sei+ 4). Bei den Schreibroutinen steht vor dem Aufruf der zu schreibende Wert in temp. Beispiel in BASIC (Bild 6):
Sie möchten wissen, welches Register welche Nummer hat? Ganz einfach (Tabelle 3):
Tabelle 3: Die Register-Nummern des Floppycontrollers)
Diese Tabelle entspricht derjenigen, die ich bereits in der vorherigen Folge für die Registernummern angegeben habe.
Damit sind Sie mit einem recht kompletten Werkzeug zur Analyse Ihrer Disketten ausgerüstet. Eigene Erweiterungen des EXTENDED MINIMON sind leicht anzufügen. Interessant wäre eine Analyseroutine, die kopiergeschützte Disketten auf Tricks abklopft. Eine Anregung, wie das zu machen wäre:
Wie Sie ja jetzt wissen, ist der READ-TRACK-Befehl des FDC ganz und gar nicht zuverlässig. Schade, denn wenn er perfekt funktionieren würde, könnten wir das Problem mit einem einzigen READ-TRACK erschlagen. Leider aber muß man sich behelfen. Immerhin hat man den READ-ADDRESS-Befehl, der Auskunft über die vorhandenen Sektorvorspänne gibt. Hat man erst mal die Nummern der Sektoren, kann man versuchen, diese Sektoren mit READ-SECTOR einzulesen. Schließlich ist es oft noch wichtig zu wissen, wie groß die Lücken zwischen den Sektoren sowie zwischen Vorspann und Sektor sind. Das kann man nun ungefähr ermitteln, indem man die Spur zusätzlich mit READ-TRACK einliest und die Abstände zwischen den Syncbytes mißt (spätestens das dritte Syncbyte liest der Controller nämlich korrekt ein, auch bei READ-TRACK!).
POKE SEL + 3,9 ! EXTENDED-Modus für 'Register schreiben’
DPOKE SEL + 4,128 ! Kommandoregister selektieren
DPOKE SEL + 8,33 ! Zu schreibender Wert (Step-Befehl)
CALL SEL
Bild 6: Alle Register gezogen
Trotzdem ist beim WD1772, dem Controller des ST, oft viel Hirnschmalz nötig, bis man aus den ulkigen, beinahe zufälligen Mustern, die der READ-TRACK-Befehl liest, die Kopierschutztricks rekonstruiert hat. Individuelle Genialität hat hier also noch Chancen, ein Grund, warum die Knackerei auf dem ST noch nicht ausgestorben ist. Am Besten wäre es natürlich, wenn kein Kopierschutz mehr nötig wäre, aber bis das erreicht ist, werden uns die Softwarehersteller wohl noch vor so manche Denksportaufgabe stellen. Manche Leute, die es wissen müssen, meinen, daß man beim Knacken am Schnellsten seinen Rechner kennenlernt.
Wie gesagt, dies ist vorläufig die letzte Folge des Floppykurses. Für die angekündigten Vorhaben wie das Kopierprogramm für HYPER-FORMAT-Disketten, das superschnelle Formatierprogramm und andere Gags brauche ich zunächst ein bißchen Freiraum. Was von meinen Projekten Realität wird, können Sie in unregelmäßigen Abständen in der ST lesen. Keep hacking!
' Erweiterung fUr den EXTENDED MINIMON
' aus der ST 10/87
' zusätzliche Registerroutinen
' erweitertes SELECT.S in den Datazeilen
'
' Written 1987 by Claus Brod
' Am Felsenkeller 2
' 8772 Marktheidenfeld
' Written in GfA-Ba6ic
Procedure Register
' sel+3:8 für lesen, 9 für schreiben, 10 für DMA-Status
' in sel+4 wird das Register übergeben
' in sel+8 wird der Wert übergeben
Print R$;"Register";O$
Print R$;"L";O$;"esen oder ";R$;"S";O$;"chreiben?"
Repeat
A$=Upper$(InputS(1))
Until A$="L" Or A$="S"
If A$="L"
@Get_them_al1
Else
Op%=9
Poke Sel+3,Op%
Print "(1) Kommandoregister"
Print "(2) Trackregister"
Print "(3) FDC-Sektorregister"
Print "(4) Datenregister"
Print "(5) DMA-Sektorregister"
Repeat
A=Val(Input$(1))
Until A>0 And A<6
R%=(A-1)*2
If A=5
R%=16
Endif
Dpoke Sel+4,128+R%
If Op%=9
Input "Neuer Wert für das Register";W%
Dpoke Sel+8,W%
Endif
Call Sel
Print "Opcode: $";Hex$(Peek(Se1+3))
Print "Register: $";Hex$(Dpeek(Sel+4))
Print "Wert: S"5Hex$(Dpeek(Se1+8) And 255)
Print
Endif
Return
'
Procedure Get_them_a11
Poke Sel+3,8 ! Register lesen
Dpoke Sel+4,128+0
Call Sel
Print "FDC-Statusregister: $";Hex$(Dpeek(Se1+8) And 255)
Dpoke Sel+4,128+2
Call Sel
Print "Trackregister: $";Hex$(Dpeek(Sel+8) And 255)
Dpoke Sel+4,128+4
Call Sel
Print "FDC-Sektorregister: $";Hex$(Dpeek(Sel+8) And 255)
Dpoke Sel+4,128+6
Call Sel
Print "Datenregister: S";Hex$(Dpeek(Sel+8) And 255)
Dpoke Sel+4,128+16
Call Sel
Print "DMA-Sektorregi6ter: $";Hex$(Dpeek(Se1+8) And 255)
Poke Sel+3,10 ! DMA-Status lesen
Call Sel
Print "DMA-Status: $";Hex$(Dpeek(Se1+8) And 255)
Return
Select:
Data 60,E,0,0,0,0,0,0,0,0.0,0.0,0,0,0
Data 48,E7,FF,FE,42,80,61,6A,45,FA,FF,F2,24,80,50,F9
Data 0,0,4,3E,3E,3A,FF,DC,66,14,33,FC,0,80,0,FF
Data 86,6,32,39,0,FF,86,4,8,1,0,7,66,F4,C,7
Data 0,8,6C,5C,A,7,0,7,CE,3C,0,7,40,E7,0,7C
Data 7,0,13,FC,0,E,0,FF,88,0,10,39,0,FF,88,0
Data C0,3C,0,F8,8E,0,13,C7,0,FF,88,2,51,F9,0,0
Data 4,3E,46,DF,45,FA,FF,96,20,12,61,6,4C,DF,7F,FF
Data 4E,75,2F,0,3F,3C,0,20,4E,41,5C,8F,4E,75,51,C9
Data FF,FE,4E,75,2F,A,3F,3C,0,9,4E,41,5C,8F,4E,75
Data C,7,0,8,67,14,C,7,0,9,67,20,C,7,0,A
Data 67,52,45,FA,0,68,61,DC,60,BA,33,FA,FF,48,0,FF
Data 86,6,61,1C,45,FA,FF,42,34,80,60,A8,3E,3A,FF,3A
Data CE,7C,0,FF,33,FA,FF,2E,0,FF,86,6,61,14,60,94
Data 32,3C,0,1E,61,A8,30,39,0,FF,86,4,32,3C,0,1E
Data 60,9C,32,3C,0,1E,61,96,33,C7,0,FF,86,4,32,3C
Data 0,1E,60,8A,33,FC,0,90,0,FF,86,6,30,39,0,FF
Data 86,6,45,FA,FE,F4,34,80,60,0,FF,5A,52,6F,75,74
Data 69,6E,65,20,6E,6F,63,68,20,6E,69,63,68,74,20,69
Data 6D,70,6C,65,6D,65,6E,74,69,65,72,74,2E,D,A,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data
Listing 1 beinhaltet die Routinen, die den EXTENDED MINIMON (vorgestellt in der ST 7/87 und ST 9/87) um die REGISTER-Funktion erweitern. Laden Sie Ihren EXTENDED MINIMON und tippen Sie einfach die neuen Routinen dazu. Die alten SELECT-Datazeilen löschen Sie bitte und ersetzen sie durch die im Listing 1 angegebenen. Ebenso müssen Sie die Prozedur Register durch die neue ersetzen.
Mit ’R’ können Sie im Menü des Trackmonitors die neuen Routinen ansteuern. Sie können zwischen Lesen (’L’) und Schreiben (’S’) auswählen. Nach ’L’ erhalten Sie die Inhalte aller Register des Floppycontrollers. Nach ’S’ können Sie zunächst auswählen, welches Register Sie neu beschreiben wollen, bevor Sie den Wert eingeben. Nach der Eingabe bekommen Sie eine Rückmeldung.
Die Schreibroutine ist eigentlich nur der Vollständigkeit halber angefügt. Mit der Leseroutine können Sie aber beispielsweise sehr schön beobachten, welche Bits des Statusregisters der FDC nach einer Weile zurücksetzt, wenn er nichts zu tun hat. Im Menü des Trackmonitors bekommt man zwar auch eine Rückmeldung über den Status des FDC, allerdings handelt es sich hier um den Status während der Ausführung des Kommandos.
*****************************
* Erweiterung für SELECT.S
* Register des FDC und DMA-Chips
* lesen und schreiben
*
* Written 1987 by Claus Brod
* Am Felsenkeller 2
* 8772 Marktheidenfeld
*
* Assembliert mit AS68
*****************************
*****************************
* extend: Erhält Kommando in d7
*
*****************************
extend:
cmpi.b #8,d7 * Register lesen?
beq rdreg * Ja, zur Registerleseroutine
cmpi.b #9,d7 * Register schreiben?
beq wrreg * Ja, zur Registerschreiberoutine
cmpi.b #10,d7 * DMA-Status lesen?
beq dmastat * ja, zur DMA-Statusroutine
lea nochnit(pc),a2 * Routine noch nicht
bsr msg * implementiert
bra raushier * und raus
*****************************
* in parm angegebenes Register lesen
* und in temp ablegen
*****************************
rdreg:
move.w parm(pc),dmodus * Register auswählen
bsr rdfdc * FDC/DMA-Register lesen
lea temp(pc),a2 * Adresse des Ausgabefeldes
move.w d0,(a2) * Registerinhalt schreiben
bra raushier
*****************************
* in parm angegebenes Register mit dem
* Wert aus temp beschreiben
*****************************
wrreg:
move.w temp(pc),d7 * Wert holen
and.w #$ff,d7 * Nur unteres Byte beachten
move.w parm(pc),dmodus * Register auswählen
bsr wrfdc * d7 zum FDC schicken
bra raushier * und raus
*****************************
* rdfdc: FDC-Register lesen
*****************************
rdfdc:
move.w #30,d1 * 30 Schleifen
bsr time * warten
move.w daccess,d0 * Register lesen
move.w #30,d1 * 30mal rundherum
bra time * um Ulm und in Ulm und um Ulm herum
*****************************
* wrfdc: FDC-Register beschreiben
*****************************
wrfdc:
move.w #30,d1 * 30 Loops
bsr time * warten
move.w d7,daccess * d7 ins Register
move.w #30,d1 * 30 Loops
bra time * warten und raus
*****************************
* dmastat: DMA-Status lesen
*****************************
dmastat:
move.w #$90,dmodus * DMA-Sektorregister anwählen
move.w dmodus,d0 * DMA-Status lesen
lea temp(pc),a2 * Adresse des Ausgabefeldes
move.w d0,(a2) * Status merken
bra raushier * und raus
Wie schon im Artikel beschrieben, nutze ich für die Erweiterung der SELECT-Routine aus der ST9/87 die ’open ends’ aus, die ich absichtlich in dieser Routine belassen hatte.
Laden Sie die alte SELECT-Routine in Ihren Editor, löschen Sie die alte EXTEND-Routine und ersetzen Sie sie durch die neue. Die übrigen Routinen werden einfach nur hinzugefügt. Danach kann man mit den fiktiven Laufwerksnummern 8,9 und 10 die zusätzlichen Funktionen aufrufen (siehe Haupttext).
Das Konzept der fiktiven Laufwerksnummern ist natürlich beliebig erweiterbar, Sie können in der EXTEND-Routine ohne Schwierigkeiten zum Beispiel die imaginäre Drivenummer 11 verwenden, um eine eigene Routine anzuspringen, sei es eine Analyseroutine für einen Track oder ein kleines Kopierprogramm oder was auch immer. Lassen Sie sich was einfallen!