In die Geheimnisse der Floppy eingetaucht (Teil 2)
Diese Folge befaßt sich mit dem Befehlssatz der VC 1541 und deren Meldungen an den Computer. Sie werden erkennen, daß Sie neben Ihrem C 64 noch einen anderen vollständigen Computer vor sich haben, der nicht nur als einfaches und »dummes« Peripheriegerät verstanden werden will.
Sicherlich machte sich mancher Floppybesitzer, der ein schnelleres Peripheriegerät als die Datasette haben wollte, schon seine Gedanken über den Preis der VC 1541: »Die kostet ja mehr als der Computer!«. In der Tat ist die VC 1541 von dieser Seite her betrachtet nicht gerade günstig, wer sich jedoch schon intensiver mit ihr beschäftigt hat, wird eine Eigenart festgestellt haben, die sie mit allen CBM-Floppys teilt: Es handelt sich hier um sogenannte Floppystationen, nicht nur um Laufwerke. Das bedeutet, diese Geräte besitzen ein eigenes Betriebssystem (DOS) und eigene Mikroprozessoren. Sie arbeiten völlig unabhängig vom Computer und dessen Speicher. Der Vorteil liegt auf der Hand: Die Floppy beansprucht weder Speicherplatz noch Rechenzeit des Computers, außer beim direkten Datenaustausch. Als Beispiel betrachte man den Befehl »N:« (Formatieren). Während der Formatierung steht der Computer zur (fast) freien Verfügung, da dieser Vorgang nur floppyintern abläuft und sich der C 64 mit READY meldet, während die 1541 noch arbeitet.
Wir wollen uns jedoch nur den Direktzugriffsbefehlen und den Speicherbefehlen widmen; auch übergehen wir die im Commodore-Handbuch nicht erwähnte relative Datenspeicherung, über die in anderen Ausgaben schon ausführlich gesprochen wurde. Uns sollen nur die Befehle beschäftigen, die uns zur willkürlichen Manipulation von Floppystation und Disketten nützen.
Zur Beruhigung: Ein Beschädigen der 1541 durch direkte Eingriffe in das DOS ist nicht zu befürchten, auch wenn es passieren kann, daß sich die Floppy nur durch Aus-/ Einschalten wieder in den Normalzustand versetzen läßt. Haben Sie übrigens einmal, wie in der letzten Folge empfohlen, das Formatkennzeichen einer Diskette verändert? Sie werden sicherlich bemerkt haben, daß sich danach nichts mehr auf Ihre Diskette schreiben läßt. Mit diesem Trick, der die gleichen Folgen wie das Anbringen einer Schreibschutzplakette an der Diskette hat, können Sie sich also ganz einfach Ihre Diskette gegen unbeabsichtigtes Löschen sichern. ACHTUNG: Diese Methode funktioniert natürlich nicht, wenn neu formatiert werden soll; hiergegen hilft nur das Anbringen einer Schreibschutzplakette!
Die Floppystation verfügt über, außer den schon bekannten Befehlen zur Diskettenorganisation, noch eine ganze Anzahl weiterer Befehle, mit denen sich ungeahnte Möglichkeiten ergeben, zum Beispiel Herstellen eines eigenen Diskettenformats, Leseschutz von Disketten, Programmschutz, Modifikation der Lade- und Saveroutinen und, und… Dafür ist es allerdings nötig, daß wir diese Befehle Schritt für Schritt kennen lernen, bevor wir auf die Tricks der Profis, die Manipulationen des DOS und den gezielten Eingriff in den Programmablauf der Floppystation zu sprechen kommen. Dafür ist allerdings das Beherrschen des C 64 und der Maschinensprache unerläßlich. So lohnt es sich unter Umständen, nachdem man aus Basic nichts mehr herausholen kann, den Einstieg in die Assemblerprogrammierung zu wagen. Sehr gute Literatur dafür ist vorhanden. Aber diesmal wollen wir uns noch auf Basic beschränken, um Sie mit dem Befehlssatz der Floppy vertraut zu machen.
Wie schon erwähnt, handelt es sich bei der 1541 um einen vollständigen Computer, der ebenso wie Ihr C 64 RAM und ein Betriebssystem (DOS) im ROM besitzt.
Die genaue Aufteilung ist in Bild 1 zu sehen. Jetzt soll uns nur der RAM-Bereich interessieren (Bild 2). Nicht nur auf der Diskette, sondern auch im RAM werden Speicherbereiche in Abschnitte zu jeweils 256 Byte aufgeteilt. Sie heißen dann nicht mehr BLOCKS sondern PAGES (Seiten). Das RAM der 1541 umfaßt nun genau 8 PAGES, durchnumeriert von 0 bis 7, insgesamt als 2 KByte. Die Page Nr. 0 (auch Zero-Page genannt) wird hier, wie auch im C 64, vom Betriebssystem als Arbeitsspeicher benutzt und steht uns deshalb nicht zur freien Verfügung. Ähnlich verhält es sich mit den Pages 1 und 2. Die Pages 3 bis 7 stellen sogenannte Pufferspeicher dar; hier werden alle Daten, die von der Diskette gelesen beziehungsweise auf sie geschrieben werden, zwischengespeichert, da nur blockweise gelesen oder geschrieben werden kann.


Soll zum Beispiel nur ein einziges Byte auf der Diskette geändert werden, so wird erst der gesamte Block in einen der 5 Pufferspeicher gelesen, dort abgeändert und schließlich komplett wieder zurückgeschrieben. Aus diesen Gründen ist es also notwendig, daß wir uns vor einem Direktzugriff einen der Puffer reservieren, in dem dann gearbeitet wird.
Mit Hilfe des »Open«-Befehls eröffnen wir einen Direktzugriffskanal. Die Syntax lautet wie folgt:
OPEN fn, gn, kn, ”#”
Hierbei bedeuten:
- fn — Filenummer (1-127)
- gn — Gerätenummer (norm. 8)
- kn — Kanalnummer in der Floppy (2-14)
Diese Abkürzungen werden wir im folgenden immer verwenden! Ein Beispiel:
OPEN 1, 8, 2, ”#”
Diese Anweisung eröffnet im Computer ein File mit der Nummer 1, adressiert als Gerät die Floppy (Nummer 8) und reserviert in der 1541 einen Kanal (Nummer 2), dem ein Puffer zugeordnet wird. Mit den floppyinternen Kanälen verhält es sich wie folgt: Es stehen insgesamt 16 Kanäle zur Verfügung. Hierbei sind Kanal 0 und 1 für LOAD und SAVE reserviert, Kanal 15 ist der Kommandokanal, den Sie bisher immer benutzt haben, um Befehle (zum Beispiel Formatieren) an die Floppy zu senden und die Fehlermeldungen der Floppy zu empfangen.
Für unsere Zwecke stehen also noch die Kanäle 2 bis 14 zur Verfügung. In unserem Fall reserviert die Floppy den nächsten freien Puffer. Will man jedoch einen bestimmten Puffer reservieren, etwa um dort ein Maschinenprogramm abzulegen, so ist es notwendig, der 1541 mitzuteilen, welcher Puffer gewünscht wird:
OPEN1,8,2,"#1"
Es ist hier allerdings zu beachten, daß der gewählte Puffer nicht schon belegt ist; in diesem Fall gibt die Floppy eine Fehlermeldung aus. Wollen Sie an dieser Stelle mehr über das Auslesen der Fehlermeldungen und deren Bedeutung wissen, können wir Sie hier beruhigt auf das Commodore-Handbuch verweisen. Im allgemeinen sind Puffer 4 für die BAM und Puffer 3 für das Directory reserviert. Haben Sie die Wahl des Puffers der Floppy überlassen, so erfahren Sie die gewählte Nummer durch Auslesen des soeben geöffneten Direktzugriffskanals:
10 OPEN 1,8,2,”#"
20 GET#1,D$
30 D = ASC (D$ + CHR$(0))
40 REM Puffernummer in D
Die BLOCK-Befehle
- Der BLOCK-READ-Befehl (B-R):
Mit dem BLOCK-READ-Befehl liest man jeden beliebigen Block von Diskette in einen vorher reservierten Puffer. Die Syntax lautet:
PRINT#fn,"B-R";kn;dn;t;s
- dn — Drivenummer (immer 0)
- t — Tracknummer
- s — Sektornummer
Diese Befehlsfolge liest den Block 18,0 von der Diskette in den oben reservierten Puffer. Wie man sieht, können anstelle der CHR$-Codes feste Zahlenwerte in den Befehlsstring mit übernommen werden. Das ganze hat bloß einen kleinen Schönheitsfehler. Mit dem B-R-Befehl läßt sich das erste Byte eines Blocks nicht lesen. Deshalb benutzt man normalerweise anstatt des B-R-Befehls den U1-Befehl. Dieser hat exakt die gleiche Syntax und kann in jedem Fall benutzt werden:
PRINT#15,"U1 2 0 18 0”
Auf diese USER-Befehle kommen wir später zurück. Mit einer GET#-Schleife lassen sich nun die einzelnen Bytes in den Computer einlesen. - Der BLOCK-WRITE-Befehl (B-W):
Hiermit lassen sich die Daten aus dem reservierten Puffer wieder auf die Diskette schreiben. Syntax:
PRINT#fn,”B-W”;kn;dn;t;s
Beispiel: PRINT#15,"B-W 2 0 18 0”
Natürlich gibt es analog zum B-R einen USER-Befehl ; U2.
Beispiel: PRINT#15,"U2 2 0 18 0". - Der BUFFER-POINTER-Befehl (B-P):
Für jeden Puffer gibt es einen Zeiger, den BUFFER-POINTER. Dieser zeigt auf das aktuelle Byte im Puffer und wird bei jedem Datenzugriff um Eins erhöht, damit man alle 256 Bytes eines Blocks der Reihe nach lesen kann. Dieser Pointer wird mit dem B-P-Befehl gezielt auf bestimmte Bytes positioniert, wenn man nur einzelne Werte und nicht den gesamten Block lesen will. Syntax:
PRINT# fn,"B-P"; kn; position
Beispiel:
Wir möchten in die Variable A den Wert des 123. Bytes von Block 1;16 einlesen:10 OPEN15,8,15 20 OPEN1,8,2,"#" 30 PRINT#15,"U1 2 0 1 16” 40 PRINT#15,"B-P2 122” 50 GET#1,A$ 60 A = ASC(A$ + CHR$(0))
Als weiteres Beispiel dient Listing 1. - Der BLOCK-ALLOCATE-Befehl (B-A):
Wenn Sie im Direktzugriffsverfahren eine Diskette beschreiben, muß in der BAM danach auch verzeichnet werden, daß die entsprechenden Blocks mit Daten gefüllt sind und nicht mehr überschrieben werden dürfen. Dazu dient der B-A-Befehl, der jeden beliebigen Block in der BAM als belegt kennzeichnet. Die Syntax lautet:
PRINT#fn,"B-A";dn;t;s
Beispiel:
PRINT#15,"B-A0 1 16”
kennzeichnet Block 1;16 als belegt; war dieser Block schon belegt, meldet sich die Floppy mit der Fehlermeldung »65,NO BLOCK,XX,YY«; wobei XX und YY die Track- und Sektornummer des nächsten freien Blocks angeben. - Der BLOCK-FREE-Befehl (B-F):
Dieser ist das genaue Gegenstück zum B-A-Befehl; er deklariert einmal belegte Blöcke wieder als frei für einen weiteren Zugriff. Seine Syntax ist identisch mit der des B-A-Befehls.
- Der BLOCK-EXECUTE-Befehl (B-E):
Dieser Befehl nimmt eine Sonderstellung ein. Er gleicht im Prinzip dem B-R-Befehl; nur mit dem zusätzlichen Effekt, daß der eingelesene Block im Puffer als Maschinenprogramm gestartet wird.
Zur Vertiefung der Block-Befehle sei noch auf die Listings 2 bis 5 hingewiesen, welche die eben besprochenen Anwendungen noch an praktischen Beispielen verdeutlichen.
Die MEMORY-Befehle
- Der MEMORY-READ-Befehl (M-R):
Dieser Befehl entspricht haargenau dem PEEK-Befehl in Basic. Mit ihm können Sie jede beliebige Speicherstelle der Floppy auslesen. Syntax:
PRINT#fn,"M-R";CHR$(adl);CHR$ (adh);CHR$(n)
- adl = Low-Byte
- adh = High-Byte
- n = Anzahl (0 bis 255)
Abgeholt werden die gelesenen Daten ebenfalls über den Kommandokanal mit GET#.
Beispiel: Lesen der beiden ID-Zeichen im ASCII-Format der zuletzt initialisierten Diskette:
10 OPEN15,8,15 20 PRINT#15,"M-R"CHR$(18)CHR$ (0)CHR$(2) 30 GET#15,A$,B$ 40 PRINTA$;B$
Diese Routine liest die Zero-Page Adressen 18 und 19, in denen die entsprechenden Werte gespeichert sind. In Tabelle 1 sind einige der wichtigsten Zero-Page Adressen aufgeführt.
- Der MEMORYWRITE-Befehl (M-W):
Dieses Kommando kann als POKE-Befehl in den Floppy-Speicher angesehen werden. Die Syntax ist hier wie folgt:
PRINT#fn, "M-W";CHR$(adl)CHR$ (adh)CHR$(n)CHR$(data1)CHR$(data2)…
- Der MEMORY-EXECUTE-Befehl (M-E):
Auch dieser Befehl ist äquivalent zu einem Basic-Befehl, dem SYS-Befehl. Mit ihm kann man also ein Maschinenprogramm an einer beliebigen Stelle im Floppy-Speicher ausführen. Syntax:
PRINT#fn,"M-E"CHR$(adl)CHR$(adh).
Siehe auch Listing 7.
Die USER-Befehle
Die USER-Befehle stellen eine Erweiterung des Befehlssatzes dar, der fast ausschließlich der Bequemlichkeit dient. Ul und U2 wurden schon besprochen, sie ersetzen B-R und B-W. Die Befehle U3 bis U8 dienen dem Start eines Maschinenprogramms im Floppy-Speicher, dessen Anfangsadressen in einer Tabelle abgelegt sind, so entsprechen:
- U3 einem Start bei $0500
- U4 einem Start bei $0503
- U8 einem Start bei $050F.
- U4 ersetzt also beispielsweise den Befehlsstring: M-E CHR$(3)CHR$(5). U9 zeigt auf den NMI-Vektor der 1541, welcher allerdings eine Sonderfunktion hat: Mit U9+ wird die Floppy auf C 64- und mit U9- auf VC 20-Betrieb umgeschaltet.
- U: stellt einen Reset dar, ähnlich dem SYS 64738 beim C 64.
Mit den Kenntnissen über den Befehlssatz der VC 1541 dürfte es Ihnen nun keine Schwierigkeiten mehr bereiten, sich das Programm EDDI einmal zu Gemüte zu führen. Das einzig Besondere daran sind die Routinen zum Lesen und Schreiben eines Blocks, die aus Geschwindigkeitsgründen in Maschinensprache geschrieben sind. Ein großer Teil der in diesen Folgen erwähnten Informationen ist auch im Commodore-Handbuch enthalten, nur sind dort oft Fehler.
Wir wollen uns für diese Folge von Ihnen verabschieden, nicht ohne Sie dringend dazu anzuhalten zu probieren.
(Schramm/Schneider/gk)100 rem aenderung von id, formatkenn- 101 rem zeichen & leerzeichen zwischen 102 rem diesen beiden.(insg. 5 zeichen) 103 rem bsp: alte id :xy 2a 104 rem id^ ^formatkennz. 105 rem kann auf :hallo 106 rem geaendert werden. das leerz. 107 rem wird hier zum ersten 'l' 108 rem wirkt sich nur auf direct. aus! 109 : 110 open 15,8,15,"i":open1,8,2,"#" 120 print#15,"u1 2 0 18 0" 130 print#15,"b-p 2 162" 140 get#1,a$,b$,c$,d$,e$ 150 print a$;b$;c$;d$;e$ 160 input"neu:";n$ 170 print#15,"b-p 2 162" 180 print#1,n$; 190 print#15,"u2 2 0 18 0" 200 print#15,"i" 210 close 8:close 15
1000 rem unterprogramm 1 1001 rem lesen eines eintrages aus dem 1002 rem directory (alle 30 bytes !!!) 1003 rem in die variable dd$ 1004 rem uebergabeparameter: 1005 rem mm=nummer des eintrages der 1006 rem gelesen werden soll 1007 : 1008 : 1009 : 1010 open 15,8,15,"i":open8,8,8,"#" 1020 nn$="":fori=1to30:nn$=nn$+chr$(0):nexti 1030 xx=int((mm-1)/8) 1040 print#15,"u1 8 0 18 0" 1050 forzz=1toxx+1 1060 print#15,"b-p 8 0" 1070 get#8,tt$:tt=asc(tt$+chr$(0)) 1080 get#8,ss$:ss=asc(ss$+chr$(0)) 1090 if tt=0 then dd$=nn$:goto1170 1100 print#15,"u1 8 0";tt;ss 1110 nextzz 1120 pp=mm-(xx*8):pp=(pp-1)*32+2 1130 print#15,"b-p 8";pp 1140 forzz=1 to 30:get#8,zz$ 1150 ifzz$=""thenzz$=chr$(0) 1160 dd$=dd$+zz$:nextzz 1170 close 8:close 15 1180 return
2000 rem unterprogramm 2 2001 rem schreiben eines eintrages in 2002 rem das directory (30 bytes !!!) 2003 rem uebergabeparameter: 2004 rem mm=nummer des eintrages der 2005 rem geschrieben werden soll 2006 rem dd$=directoryeintrag 2007 : 2008 : 2009 : 2010 open 15,8,15,"i":open8,8,8,"#" 2020 xx=int((mm-1)/8) 2030 print#15,"u1 8 0 18 0" 2040 forzz=1toxx+1 2050 print#15,"b-p 8 0" 2060 get#8,t$:tt=asc(t$+chr$(0)) 2070 get#8,s$:ss=asc(s$+chr$(0)) 2080 if tt=0 then 2150 2090 print#15,"u1 8 0";tt;ss 2100 nextzz 2110 pp=mm-(xx*8):pp=(pp-1)*32+2 2120 print#15,"b-p 8";pp 2130 print#8,dd$; 2140 print#15,"u2 8 0";tt;ss 2150 close 8:close 15 2160 return
100 rem beispiel fuer eine kleine 101 rem directory-manipulation: 102 rem scratch-schutz einzelner files 103 rem nach anzeige des filenamens: 104 rem j = schuetze dies file 105 rem n = weiter zum naechsten file 106 rem e = ende 107 rem achtung !!! "schuetzt" auch 108 rem schon gescratchte files wenn 109 rem verlangt, stellt sie aber nicht 110 rem wieder her !!! 111 rem scratch-schutz wird im direct. 112 rem durch ein '<' hinter dem 113 rem filetyp angezeigt. naeheres 114 rem siehe tabelle folge 1 !!! 115 rem achtung !!! nur zusammen mit 116 rem den unterprogrammen 1 & 2 117 rem lauffaehig !!! 118 : 119 : 120 mm=0 130 mm=mm+1:dd$="":gosub1000 140 if dd$=nn$thenend 150 printmid$(dd$,4,16):inputaa$ 160 if aa$="e"then end 170 if aa$="n"then 130 180 hh$=left$(dd$,1) 190 hh$=chr$(asc(hh$)or2^6) 200 dd$=hh$+right$(dd$,29) 210 gosub2000 220 goto 130 230 end
100 rem schreibschutz setzen 101 rem durch aenderung des format- 102 rem kennzeichens in der bam !!! 103 rem funktionsweise : 104 rem formatkennzeichen wird auf 105 rem beliebigen wert ausser 'a' 106 rem gesetzt. ab sofort koennen 107 rem keine schreibvorgaenge ausser 108 rem formatieren durchgefuehrt 109 rem werden. also vorsicht ! 110 : 200 open 15,8,15,"i":open8,8,8,"#" 210 print#15,"u1 8 0 18 0" 220 print#15,"b-p 8 2" 230 print#8,"x"; 240 print#15,"u2 8 0 18 0" 250 print#15,"i" 260 close8:close15:end
100 rem directory-sorter 101 rem sortiert directory alphabetisch 102 rem bei vielen eintraegen bitte 103 rem etwas geduld (max. 5.min) 104 rem sortiert auch gescratchte files 105 rem mit, stellt sie aber nicht 106 rem wieder her ! sortieralgorithmus 107 rem kann sich in einem solchen fall 108 rem in einer endlosschleife ver- 109 rem heddern. abhilfe: nach 3-4 min. 110 rem stop-taste druecken, dann 111 rem goto 210 eingeben. sind eintr. 112 rem dann noch nicht vollkommen sor- 113 rem tiert, nochmals fuer einige 114 rem minuten laufen lassen. 115 rem achtung !!! nur zusammen mit 116 rem den unterprogrammen 1 & 2 117 rem ablauffaehig !!! 118 : 119 : 120 dimdd$(144) 130 mm=mm+1:gosub1000 140 if dd$=nn$thenmm=mm-1:goto160 150 dd$(mm)=dd$:dd$="":goto130 160 for gg=1tomm-1 170 if mid$(dd$(gg),4,16)<mid$(dd$(gg+1),4,16)then 190 180 hh$=dd$(gg):dd$(gg)=dd$(gg+1):dd$(gg+1)=hh$:ff=1 190 next gg 200 if ff then ff=0:goto160 210 ii=mm 220 formm=1toii:dd$=dd$(mm):gosub2000:nextmm 230 end
100 rem beispiel fuer memory-execute 101 rem loest in der floppy langsames 102 rem blinken der roten led (kenn- 103 rem zeichnet normalerweise hard- 104 rem ware-fehler) aus. 105 rem kann nur durch ausloesen eines 106 rem resets entweder durch ein/aus- 107 rem schalten der floppy oder des 108 rem computers beendet werden. 109 rem einsprungsadresse : $ea6e 110 : 120 open15,8,15 130 print#15,"m-e"chr$(110)chr$(234) 140 close 15 150 end