Nachdem in der letzten Folge kräftig über Adressen und Bit’s sowie Komandos und Register gesprochen wurde, wird diesmal der TRACKMON vorgestellt. Es handelt sich dabei um eine Erweiterung des MINIMON. Verwendet werden die zwei Routinen vom letztenmal, SELECT und LOCKSLEY, diesmal allerdings als Data-Zeilen.
Im Listing des MINIMON, der in der ST Computer Juli/August vorgestellt wurde, hatte ich ja eigens eine Auswahl zwischen Sektormonitor und Trackmonitor vorgesehen. In diesem Heft finden Sie nun die Prozeduren, die Sie brauchen, um aus dem MINIMON den EXTENDED MINIMON zu machen. In dem Programm von damals müssen allerdings drei kleine Eingriffe gemacht werden:
Die Routine DUMP benötigt jetzt einen Parameter. Ändern Sie also den Prozedurkopf Procedure Dump in Procedure Dump(L) ab. Weiterhin ändern Sie in der siebten Zeile die For-Anweisung in For T=1 To L Step 16 ab.
Außerdem müssen Sie in der Procedure Sektormon noch etwas drehen: Statt If M$ = ”D” @ Dump Endif, muß es heißen If M$ = ”D” @ Dump(512) Endif (natürlich müssen die einzelnen Anweisungen in getrennte Zeilen!).
Wenn Sie statt der Datazeilen lieber die passenden Assemblerprogramme abtippen wollen, können Sie sich auch die Zahlenwüste sparen und statt dessen das assemblierte LOCKSLEY.PRG mit dem ebenfalls abgedruckten PRGTOLST.BAS in die Datazeilen wandeln lassen. Die müssen Sie dann hinter das Label Locksley: ’mergen’. Ähnliches gilt für SELECT.S und das Label Select:.
Den Trackmonitor des MINIMON verstehe ich hauptsächlich als Demonstration der Floppyprogrammierung und nicht als Beispiel für strukturierte und besonders einwandfreie Programmiertechnik. Zudem können Sie Er soll zur Anregung dienen. Zudem können Sie mit dem EXTENDED MINIMON erste Experimente machen, die Sie tiefer ins Reich des Lesekopfes führen.
Wenn Sie beim Start des EXTENDED MINIMON ’T’ für ’Trackmoni-tor’ drücken, dauert es einen Moment, bis das Programm die Datazeilen eingelesen hat. Dann führt der Trackmonitor zuerst einen ’Restore’-Befehl aus, um den Lesekopf auf Track 0 und damit auf eine definierte Position zu bringen. Sie bekommen dann ein Menü samt ausführlicher Statusübersicht.
In der Statusübersicht werden die Ein/ Ausgabeparameter von LOCKSLEY angezeigt. Die Bedeutung der Felder hängt im allgemeinen auch vom jeweiligen Befehl ab, nachzulesen in der Anleitung zu LOCKSLEY.S. Wie der FDC- und DMA-Status decodiert wird, erfahren Sie in der nächsten Ausgabe, in der EXTENDED MINIMON um die ’Register’-Routine erweitert wird, die eine Mehrfachfunktion haben wird:
Zum einen wird man mit ihr jedem Register des Floppycontrollers auf die Finger (sorry, auf die Bits) schauen können, zum anderen zeigt sie ausführlich an, was die Fehler/Statuscodes im DMA- und FDC-Status bedeuten.
Nun eine Liste der Funktion des Trackmon und Erläuterungen dazu:
Diese Funktion schickt einen Restore-Befehl an den Diskcontroller. Dieser fährt dann den Lesekopf des Laufwerks auf die Spur 0 zurück.
"1"
STEP läßt den Lesekopf eine Spur weit in die bisher eingeschlagene Richtung fahren.
"2"
STEP-IN fährt den Lesekopf eine Spur nach innen, zu höheren Tracknummern hin.
"3"
STEP-OUT fährt den Lesekopf eine Spur nach außen.
"5"
RD SEC liest einen Sektor auf der Spur, der im Moment eingestellt ist. Dazu müssen Sie dem Trackmon sagen, welchen Sektor und wieviel Bytes Sie lesen wollen. Das können Sie benutzen, um einen Sektor nur zum Teil oder auch Sektoren mit 1024 Bytes zu lesen (auch das gibt es!), bzw. auch
"4"
SEEK fährt eine bestimmte Spur an, die Sie eingeben müssen.
mehrere Sektoren hintereinander zu lesen. Der Inhalt der eingelesenen Sek-torn kommt in den String buf$ und kann mit der Funktion „D“ angezeigt werden.
„6“
WR SEC arbeitet ähnlich wie RD SEC. Mit dieser Funktion können Sie auch Sektoren schreiben, die nicht betriebssystemkonform sind; solche mit 1024 Bytes, halbe oder gleich mehrere Sektoren usw. Sie müssen die Sektornummer angeben, mit der Sie beginnen wollen,sowie die Anzahl der Bytes, die geschrieben werden sollen. Tatsächlich geschrieben wird der Inhalt von buf$, also das, was Sie vorher mit einer Lesefunktion eingelesen und evt. per Edit verändert haben. Aus irgendwelchen Gründen müssen Sie beim Schreiben immer $20 ( = 32) Bytes mehr angeben, als in Wirklichkeit geschrieben werden sollen, eine Eigenheit des DMA-Controllers, die ich mir nicht recht erklären kann (sie gilt auch für WR TRK). Aber wenn Sie dies berücksichtigen, funktioniert die Sache einwandfrei.
"7" RD ADR liest den Vorspann eines Sektors ein. Er besteht aus Tracknummer, Seitennummer (0 = Vorderseite, 1 = Rückseite), Sektornummer, Größe des Sektors (0=128 Bytes, 1=256 Bytes, 2 = 512 Bytes, 3=1024 Bytes) und zwei Bytes Checksumme. Sie müssen angeben, wieviel Adreßfelder (so nennt man diesen Vorspann auch) Sie holen wollen. Beachten Sie hier, daß der DMA-Controller bis zu 16 Bytes intern lagert und diese gerne vergißt. Im zweiten Pufferstring, buf2$, archiviert der MINIMON (bzw. LOCKSLEY) den jeweiligen Controllerstatus pro Adreßfeld, das sind pro Adreßfeld zwei Bytes, von denen das obere unwichtig ist.
"8" RD TRK liest einen kompletten Track in buf$ ein. Vorher wird noch gefragt, wieviel Bytes dieser Spur transferiert werden sollen. Sie werden des öfteren bemerken, daß recht merkwürdige Ergebnisse beim Lesen entstehen. Das liegt daran, daß der Read-Track-Befehl im Controller selbst nicht einwandfrei funktioniert!
"9"
WR TRK schreibt eine ganze Spur, formatiert sie also. Diese Funktion habe ich bewußt recht flexibel gehalten, damit Sie beliebige Diskettenformate erzeugen können. Beim Formatieren wird der Inhalt von T$ in der Länge, die Sie angeben, auf die Spur geschrieben. Beim Write-Track-Kommando ist aber noch anzumerken, daß alle Bytes über $F5 spezielle Kommandos sind. Schauen wir uns genau an, welche Bytes man schreiben muß, um einen Sektor zu schreiben:
Sektorvorspann (einige Nullbytes)
Drei Syncbytes (erzeugt man durch drei $F5-Befehls-bytes)
Adreßmarke
(kündigt ein Adreßfeld, also den Vorspann des Sektors an und besteht aus einem einzigen $FE-Byte)
Adreßfeld
(besteht aus Angabe der Tracknummer, Seitennummer, Sektornummer und Sektorgröße, siehe RD ADR)
Checksumme
(erzeugt man durch das Befehlsbyte $F7)
Lückenbytes
(etwa 30 davon, meist etwa 20 $4E-Bytes und danach etwa 10 Nullbytes)
Drei Syncbytes
(zu erzeugen mit $F5-Befehlsbytes)
Datenmarke
(einmal $FB schreiben)
Sektordaten
(128, 256, 512, 1024 Bytes, je nach Adreßfeld)
Checksumme
(einmal $F7 schreiben)
Sektornachspann
(etwa 10 Lückenbytes, etwa $4E’s)
Bleibt anzumerken, daß in den Sektordaten selbst kein Byte über $F5 auftreten darf. Um die Trackformatierung einfach zu machen, liest die Prozedur WR TRK die Daten von dem Label ’Trackdaten:’ ein und interpretiert sie als Anweisungen, wie sie den Track erstellen soll. Das Format dieser Data-Zeilen ist „zu schreibendes Byte“ (in Hexadezimalcode), „, Anzahl“ (das heißt: wie oft soll das angegebene Byte geschrieben werden; auch diese Zahl muß in Hexadezimalcode angegeben werden). Es gibt spezielle „Bytes“ in diesen Zeilen, die besondere Bedeutung haben. Schreibt man statt eines Bytes einfach „Track“, wird der aktuelle Track eingesetzt. „Side“ fügt die aktuelle Seitennummer ein. „,“ kennzeichnet das Ende der Daten. Ein Beispiel-Track ist schon angegeben, der eine wilde Kombination von Sektoren auf die Spur schreibt, unter anderem auch einen Sektor, der nicht vollständig ist, aber durchaus Daten enthalten kann.
„A“
IRQ unterbricht den Floppycontroller bei seiner momentanen Arbeit. Da aber der Controller eh schon fix und fertig ist, wenn man im Menü gelandet ist, darf man diesen Menüpunkt nur als „der Vollständigkeit und Demo halber“ verstehen. Danke.
„D“
DUMP BUF listet einen Puffer aus. Vorher wird man gefragt, welcher Puffer angezeigt werden soll. Puffer 2 ist nur nach RD ADR gefüllt, alle anderen Lesebefehle kleistern Puffer 1 zu. SPACE, während des Listens hält die Ausgabe an, jede andere Taste bricht das Listen ab. Nach dem Ende der Ausgabe wird man gefragt, ob man den Puffer edieren will. Wenn ja, wird man (wie bei der Edit-Option des Sek-tormon) gefragt, welches Byte man ändern möchte und welchen Wert man dafür einsetzen will.
„R“
REGISTER, wird in der nächsten ST ergänzt.
„S“
SEITE, wechselt die aktuell eingestellte Seite. War sie vorher 0 (Vorderseite), so ist sie nachher 1 (Rückseite). Und umgekehrt.
„P“
schaltet den Drucker an und aus (wie beim Sektormon). DUMP BUF leitet die Ausgabe entsprechend um.
„Q“
QUIT, dürfte klar sein - Ausstieg in die Auswahl Sektormon/Trackmon/ Quit.
Schließlich noch ein paar Tips, was Sie ausprobieren könnten. Schreiben Sie mal den im Listing angegebenen Beispieltrack auf eine Diskette (die aber möglichst leer sein sollte, sonst verfluchen Sie mich noch). Versuchen Sie danach, im Trackmonitor die Sektoren einzulesen, auch den falsch geschriebenen zweiten (er ist mit 128 Bytes angekündigt, es sind aber nur 64 Bytes geschrieben). Das wird ihnen ohne Probleme gelingen, wenn Sie die richtige Bytezahl angeben. Versuchen Sie es danach mit dem Sektormon. Na? Sie bekommen einen Status von -11. Das Betriebssystem wäre an dieser Stelle schon ausgestiegen.
Ach ja, diese Fehlercodes im Sektormon sollte ich vielleicht auch noch nachtragen:
Status | Fehler |
---|---|
0 | alles klar |
-1 | nicht genau bestimmter Fehler |
-2 | Laufwerk nicht bereit |
-3 | Befehl nicht bekannt |
-4 | Prüfsummenfehler |
-5 | Befehl so nicht gültig |
-6 | Kann Track nicht finden |
-7 | Bootsektor ziemlich kaputt |
-8 | Kann Sektor nicht finden |
-9 | nicht genau bestimmter Fehler |
-10 | Fehler beim Schreiben |
-11 | Fehler beim Lesen |
-12 | nicht genau bestimmter Fehler |
-13 | Schreibschutz an |
-14 | Diskette gewechselt |
-15 | Kann Gerät nicht ansprechen |
-16 | Verify Error |
-17 | Keine Diskette eingelegt |
Diese Fehlercodes haben aber nichts (oder nur wenig) mit dem Statusbyte des Floppycontrollers zu tun.
Das Betriebssystem (und ältere Kopierprogramme) kopieren halbe Sektoren nicht oder nur fehlerhaft. In einem Kopierschutz könnten Sie nun abfragen, ob der halbe Sektor so vorhanden ist, wie Sie ihn geschrieben haben. Wenn nicht - BRTZL!
Sie können auch mal probieren, was passiert, wenn Sie den Abstand zwischen Sektorvorspann und Sektor selbst sehr groß machen. Der Sektormon meldet auch hier einen Fehler, obwohl eigentlich der ganze Sektor da ist!
Oder schreiben Sie mal zwei Sektoren mit den gleichen Sektornummern. Ein „dummes“ Kopierprogramm kopiert garantiert nur einen der beiden. Sie aber prüfen in Ihrem Programm, ob das doppelte Lottchen da ist, mit den beschriebenen Konsequenzen.
Im Grunde können Sie mit Hilfe der Befehle READ ADDRESS, READ TRACK und READ SECTOR, die Sie jetzt im EXTENDED MINIMON zur Verfügung haben, jeden Kopierschutz zumindest analysieren; wenn Sie sich ein bißchen anstrengen, können Sie ihn dann auch kopieren. Das wird natürlich manchen Softwarehersteller gar nicht gefallen, daß jetzt -dank MINIMON - jeder ST-User in seiner Software Einblick hat. Deswegen mein Appell: Kopierschutz knacken als „Sport“ ist OK, aber Raubkopieren... Doch was ein hartgesottener Kopierer ist, wird ob dieser Zeilen auch nur in Gelächter ausbrechen. Leider. Andererseits erhoffe ich mir von der allgemeinen Verfügbarkeit von Werkzeugen wie MINIMON, daß die Hersteller den Kopierschutz irgendwann einmal ad acta legen, zur Freude der rechtmäßigen Anwender. Seriennummern in den Programmen sind sowieso viel sicherer und ungleich schwieriger zu finden als ein Diskettenkopierschutz.
Lassen Sie sich davon nicht betrüben, probieren Sie selbst neue Ideen aus (wie wär’s mit Manipulationen des Sektorvorspanns...). In der nächsten ST stelle ich die Controllerbefehle detailliert vor, dann kommt sicher noch ein größerer Ideenschwall auf Sie zu.
'
' Minimon für den ST
' Written 1987 by Claus Brod
' Am Felsenkeller 2
' 8772 Marktheidenfeld
'
Cls
Do
Print
Print "Minimon ST - (C) 1987 by Claus Brod"
Print
Repeat
Print "Sektormonitor oder Trackmonitor oder Quit (S/T/Q)?"
A$=UpperS(Input$(1))
Until A$="S" Or A$="T" Or A$="Q"
Exit If A$="Q"
If A$="S"
@Sektormon
Else
@Trackmon
Endif
Loop
End
'
'
Procedure Gibmirzeit
Alert 1,"Funktion noch nicht!implementiert.","OK",A
Return
'
' Prozedur Trackmon
' initialisiert den Trackmonitor
' zeigt in einer Schleife das Menü an, fragt auf Tastendruck ab
' und verteilt auf die Unterroutinen
'
Procedure Trackmon
@Init_trackmon
Cls
Prn%=0
Richtung%=0
Seite%=0
Drive%=0
Steprate%=1 ! 3ms Steprate
R$=Chr$(27)+"p"
O$=Chr$(27)+"q"
@Rst ! auf Track 0 zurückfahren
Do
Print
Print "****************** TRACKMON (C) 1987 Claus Brod ******************"
@Prntable
Print R$;"0";O$;" Track 0. ";R$;"1";O$;" Step , ";R$."2";O$;" Step-In, ";
Print R$;"3":O$;" Step-Out, ";R$;"4";O$;" Seek, " ;R$;"5";O$;" Rd Sec.";
Print R$;"6";O$;" Wr Sec"
Print R$;"7";O$;" Rd Adr .";R$;"8";O$;" Rd Trk. ";R$; "9";O$; " Wr Trk. ";
Print R$;"A";O$;" IRQ, ";R$;"D";O$;"ump Buf. ";R$;"S";O$;"eite (";Seite%;
Print "), ";R$;"P";O$;" rn (o";
If Prn%
Print "n)"
Else
Print "ff)"
Endif
Print R$;"R";O$;"egister, ";R$;"Q";O$;"uit"
M$=Upper$(Input$(1))
Exit If M$="S"
If M$="S"
Seite%=1-Seite%
Endif
If M$="P"
Prn%=1-Prn%
Endif
If M$="D"
@Dump_buf
Endif
If M$="A"
@Irq ! FDC unterbrechen
Endif
If M$="R"
@Register
Endif
If M$<="9" And M$>="0"
On Val(M$)+1 Gosub Rst,Step,Step_in,Step_out,Seek,Rdsec,Wrsec,Rdadr
On Val(M$)-7 Gosub Rdtrk,Wrtrk
Endif
Loop
Return
'
'
Procedure Init_trackmon
Print "Einen Moment, bitte!"
Restore Locksley
@Readprog(1000)
' LOCKSLEY.S einlesen
Inter$=Prg$
Inter=Varptr(Inter$)
Opcode=Inter+3
Restore Select
@Readprog(1000) ! Reserve für Erweiterung
' SELECT.S einlesen
Sel$=Prg$
Sel=Varptr(Sel$)
Laufwerk=Sel+3
Return
'
'
Procedure Select
Poke Laufwerk,Seite%+2 ! Laufwerk A, Seite 'Seite*'
Call Sel
Return
Procedure Deselect
Poke Laufwerk,0 ! deselektieren
Call Sel
Return
'
Procedure Mach_schon(O%)
@Select ! Laufwerk selektieren
Poke Opcode,O%
Call Inter ! Kommando ausführen
@Deselect ! Laufwerk abwählen
Return
'
Procedure Register
@Gibmirzeit ! noch nicht implementiert
Return
'
' Prozedur Rst
' fährt den Lesekopf auf Spur 0 zurück
'
Procedure Rst
Print R$;"Restore";O$
Track%=0
Richtung%=0
@Mach_schon(0+Steprate%) ! Restore-Befehl
Return
'
' Prozedur IRQ
' unterbricht den Floppycontroller bei der Arbeit
'
Procedure Irq
Print R$;"IRQ";O$
@Mach_schon(208) ! IRQ-Befehl
Return
'
' Prozedur Step
' fährt den Lesekopf einen Schritt in die eingeschlagene Richtung
'
Procedure Step
Print R$;"Step";O$
Track%=Track%+Richtung%
@Mach_schon(32+16+Steprate%) ! Step mit Track-Update
Return
'
' Prozedur Step_in
' fährt den Lesekopf einen Schritt nach innen
'
Procedure Step_in
Print R$;"Step-in";O$
Richtung%=1
Track%=Track%+Richtung%
@Mach_schon(64+16 + Steprate%) ! Step-in mit Update
Return
'
' Prozedur Step-out
' fährt den Kopf einen Schritt nach außen
'
Procedure Step_out
Richtung%=-1
Print R$;"Step-out";O$
Track%=Track%+Richtung%
@Mach_schon(96+16+Steprate*) ! Step-out mit Update
Return
'
' Prozedur Seek
' fährt den Lesekopf auf die gewünschte Spur
'
Procedure Seek
Print RS;"Seek";O$
Input "Zieltrack";Trk
If Trk>Track%
Richtung%=1
Else
If Trk<Track%
Richtung%=-1
Endif
Endif
Track%=Trk
Poke Inter+5,Trk ! Spurnummer abliefern
@Mach_schon(16+Steprate%) ! Seek-Befehl
Return
'
' Prozedur Rdsec
' liest Sektor(en) auf aktuellem Track ein
'
Procedure Rdsec
Print RS;"Read sector";O$
Input "Welcher Sektor";Sek
Poke Inter+7,Sek ! Sektornummer schreiben
Input "Wieviel Bytes";Laenge
Buf$=Space$(12*512)
Dpoke Inter+8,Laenge ! Länge der Übertragung
Lpoke Inter+10,Varptr(Buf$) ! Pufferadresse
@Mach_schon(128+16) ! Read multiple sectors
Return
'
' Prozedur Rdadr
' Liest Adreßfelder auf aktuellem Track ein
'
Procedure Rdadr
Print R$;"Read Adress";O$
Input "Wieviele Adreßfelder";Laenge
Dpoke Inter+8,Laenge ! Zahl der Adreßfelder
Laenge=Laenge*6 ! 6 Byte pro Adreßfeld
Buf$=Space$(512)
Lpoke Inter+10,Varptr(Buf$) ! Pufferadresse
Buf2S=Space$(100)
Lpoke Inter+14,Varptr(Buf2S) ! Pufferadresse für Status
@Mach_schon(192) ! Rd-Address-Befehl
Return
'
' Prozedur Wrsec
' schreibt Sektor(en) auf aktuellen Track
'
Procedure Wrsec
Print R$;"Write sector”;O$
Input "Welcher Sektor";Sek
Poke Inter+7,Sek ! Sektornummer
Input "Wieviel Bytes";Laenge
Dpoke Inter+8,Laenge ! Länge der Übertragung
Lpoke Inter+10,Varptr(BufS) ! Pufferadresse
@Mach_schon(160+16) ! Write multiple sectors
Return
'
' Prozedur Rdtrk
' Aktuellen Track einlesen
'
Procedure Rdtrk
Print RS;"Read Track";O$
Buf$=SpaceS(8000)
Lpoke Inter+10,Varptr(BufS) ! Pufferadresse
Input "Wieviel Bytes";Laenge
Dpoke Inter+8,Laenge ! Länge der Übertragung
@Mach_schon(192+32) ! Read Track
Return
'
' Prozedur Wrtrk
' Einen Track formatieren
'
Procedure Wrtrk
Print RS;"Write Track";O$
T$=Space$(8000)
' liest die Trackdaten ab dem Label Trackdaten ein
' Format der Datazeilen:
' zu schreibendes Byte, danach Anzahl (wie oft soll dieses Byte
' geschrieben werden)
' "***.***" heißt Schluß
Restore Trackdaten
Cn=1
Do
Read B$,ZS
@Hextodec(BS)
B=S
@Hextodec(Z$)
Z=S
Exit If B$="***"
If Upper$(B$)="TRACK"
B=Track%
Endif
If UpperS(BS)-"SIDE"
B=Seite%
Endif
For T=1 To Z
Print B'
MidS(T$,Cn,1)=Chr$(B)
Inc Cn
Next T
Loop
Print
Lpoke Inter+10,Varptr(T$) ! Pufferadresse
Input "Wieviel Bytes";Laenge
Dpoke Inter+8,Laenge ! Länge der Übertragung
@Mach_schon(15*16) ! Write Track
Return
'
' Prozedur Dump_buf
' Gibt ersten oder zweiten Puffer aus
' und erlaubt ihn zu edieren
'
Procedure Dump_buf
Repeat
Print "Ersten oder zweiten Puffer ausgeben (1/2)?"
Ch$=Input$(1)
Until Ch$="1" Or Ch$="2"
If Ch$="1"
Sec$=Buf$
@Dump(Laenge)
Else
Sec$=Buf2$
@Dump(Len(Buf2$))
Endif
Print RS;"Edit (Y/N)?";O$
A$=Upper$(Input$(1))
If A$="Y"
If Ch$="1"
Sec$=Buf$
@Edit
Buf$=Sec$
Else
Sec$=Buf2$
@Edit
Buf2$=SecS
Endif
Endif
Return
'
' Prozedur Prntable
' Statusmeldung ausgeben
'
Procedure Prntable
Inter=Varptr(InterS)
Opcode=Inter+3
Sel=Varptr(Sel$)
Laufwerk=Sel+3
Print "Opcode :";Dpeek(Inter+2);Tab(40);
If Dpeek(Inter+30)=0
Print "Kein ";
Endif
Print "Timeout!"
Print "Track :";Track%;Tab(40);
Print "Sektor :";Dpeek(Inter+6)
Print "Adresse des Puffers 1:";Lpeek(Inter+10);Tab(40);
Print "Adresse des Puffers 2:";Lpeek(Inter+14)
Print "FDC-Status :";Peek(Inter+19);Tab(40);
Print "DMA-Status :";Peek(Inter+21)
Print "Startadresse DMA :";Lpeek(Inter+22);Tab(40);
Print "Endadresse DMA :";Lpeek(Inter+26)
Print "Gelesene/geschriebene Bytes:";Dpeek(Inter+8)
Return
' Prozedur Readprog
' Liest Programm aus Datazellen ein
'
Procedure Readprog(L)
Prg$=Space$(L)
T=1
Repeat
Read A$
If A$<>"***"
@Hextodec(Upper$(A$))
MidS(Prg$,T,1)=Chr$(S)
Endif
Inc T
Until
Return
'
' Prozedur Hextodec
' Wandelt AS in Dezimal um (Ziel: S)
'
Procedure Hextodec(AS)
S=0
A$=UpperS(A$)
For I=1 To Len(A$)
A=Asc(RightS(A$,I))
If A>64
A=A-7
Endif
A=A-48
S=S+A*16^(I-1)
Next I
Return
'
'
' Sektormon: Kleiner Diskmon mit Standardfähigkeiten
'
Procedure Sektormon
Cls
Prn%=0
Status%=0
Seite%=0
Track%=0
Sector%=1
Drive%=0
Sec$=SpaceS(512)
R$=ChrS(27)+”p”
O$=Chr$(27)+“q"
@Lesen
Do
Print "******* Minimon (C) 1987 Claus Brod Status: ";Status%;" *******"
Print RS;"R":O$;"ead. ";RS;"W";O$;"rite, ";RS;"T";O$;"rk (";Track%;"), ";
Print R$;"S";O$;"eite (";Seite%;"), Se";R$;"k";O$;"tor ("; Sector%;"), ";
Print RS;"E";O$;"dit, ";RS;"D";OS;"ump, ";R$;"P";O$; rn (o";
If Prn%
Print "n), ";
Else
Print "ff), ";
Endif
Print "E";RS;"x";O$;"ec, ";RS;"Q";O$;"uit"
M$=Upper$(Input$(1))
If M$="X"
@Exe
Endif
If M$="P"
Prn%=1-Prn%
Endif
If M$="T"
Input "Track";Track%
Endif
If M$="S"
Input "Seite";Seite%
Endif
If M$="K"
Input "Sektor";Sector%
Endif
If M$="R"
@Lesen
Endif
If M$="W"
@Schreiben
Endif
If M$="E"
@Edit
Endif
If M$="D"
@Dump(512)
Endif
Exit If M$="Q"
Loop
Return
'
'
' Sektor einlesen
'
Procedure Lesen
Buffer%=Varptr(Sec$)
'
Status%=Xbios(8,L:Buffer%,L:0,Drive%,Sector%,Track%,Seite%, 1)
Return
'
'
' Sektor schreiben
'
Procedure Schreiben
Buffer%=Varptr(SecS)
Status%=Xbios(9,L:Buffer%,L:0.Drive%,Sector%,Track%,Seite%,1)
Return
'
'
' Sektorpuffer ausgeben
'
Procedure Dump(L)
If Prn%=0
Open "O",#1,"con:"
Else
Open "O",#1,"prn:"
Endif
Print #1,"Track: ";Track%;" Sektor: ";Sector%;" Seite: ";Seite%
For T=1 To L Step 16
A$=Str$(T-1)
While Len(A$)< 3
A$="0"+A$
Wend
D$ = " "
Print #1,A$;D$;
For I=0 To 15
V$=MidS(Sec$,T+I,1)
A$=Hex$(Asc(V$))
If V$<" " Or V$>"Z"
V$="."
Endif
Print #1,Right$("0"+A$,2)
D$=DS+V$
Next I
Print #1,D$
A$=Inkey$
If AS=" "
A=Inp(2)
Endif
Exit If A$>"" And A$<>“ "
Next T
Close #1
Return
'
'
' Byte Ändern
'
Procedure Edit
Input “Byte Nr.";Byte%
Print "Alter Wert ist: ";Asc(Mid$(Sec$,Byte%+1,1))
Input "Neuer Wert in dezimal oder ASCII";Wert$
Wert%=Val(Wert$)
If Wert%=0 And Left$(Wert$,1)<>"0"
Wert%=Asc(Wert$)
Endif
Mid$(Sec$,Byte%+1,1)=Chr$(Wert%)
Return
'
'
' anderes Programm ausführen
'
Procedure Exe
Fileselect Sel$
If Exist(Sel$)<>0
S=Fre(0)
Reserve 50000
Showm
Exec 0,Sel$,"",""
Reserve S-1000
Endif
Return
' Datawüste mit den Daten für LOCKSLEY und SELECT
' sowie ein paar Trackdaten
'
Locksley:
Data 60,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,48,E7,FF,FE,42,80,61,0,1,36
Data 45,FA,FF,EE,24,80,3C,3A,FF,CA,45,FA,FF,E8,34,BC
Data 0,0,45,FA,FF,DA,34,BC,0,0,50,F9,0,0,4,3E
Data 61,0,1,1E,51,F9,0,0,4,3E,45,FA,FF,04,20,12
Data 61,0,1,2,4C,DF,7F,FF,4E,75,32,3C,0,1E,61,A
Data 33,C7,0,FF,86,4,32,3C,0,1E,51,C9,FF,FE,4E,75
Data 2E,3C,0,4,0,0,8,39,0,5,0,FF,FA,1,67,60
Data 53,87,67,40,45,FA,FF,8E,4A,52,67,EA,45,FA,FF,70
Data 15,79,0,FF,86,9,0,1,15,79,0,FF,86,B,0,2
Data 15,79,0,FF,86,D,0,3,20,3A,FF,54,B0,BA,FF,5C
Data 6D,C4,IE,3C,0,D0,61,0,0,EC,45,FA,FF,58,34,BC
Data 0,0,60,IC,61,1A,IE,3C,0,D0,61,0,0,D8,45,FA
Data FF,3E,34,BC,0,1,45,FA,FF,3C,34,BC,0,0,4E,75
Data 30,39,0,FF,86,4,45,FA,FF,1A,34,80,4E,75,30,39
Data 0,FF,86,6,45,FA,FF,E,34,80,42,41,12,39,0,FF
Data 86,9,El,89,12,39,0,FF,86,B,El,89,12,39,0,FF
Data 86,D,4 5,FA,FE,F6,24,81,92,BA,FE,EC,4 5,FA,FE,DA
Data 34,81,4E,75,45,FA,FE,E0,24,87,13,C7,0,FF,86,D
Data E0,8F,13,C7,0,FF,86,B,E0,8F,13,C7,0,FF,86,9
Data 2E,3A,FE,C4,42,80,30,3A,FE,B0,DE,80,45,FA,FE,BC
Data 24,87,4E,75,2F,0,3F,3C,0,20,4E,41,5C,8F,4E,75
Data 3E,6,3A,6,8,6,0,7,66,10,CC,3C,0,F0,BC,3C
Data 0,10,67,0,1,64,66,0,1,50,8,6,0,6,66,A
Data 8,6,0,5,67,2A,66,0,0,F4,CC,3C,0,F0,BC,3C
Data 0,C0,67,0,1,66,BC,3C,0,E0,67,6A,BC,3C,0,F0
Data 67,0,0,9E,61,0,FE,B4,32,3C,0,FA,60,0,FE,BC
Data 2E,3A,FE,48,61,0,FF,6E,45,FA,FE,5A,34,BC,0,1
Data 33,FC,0,90,0,FF,86,6,33,FC,1,90,0,FF,86,6
Data 33 'FC,0,90,0,FF,86,6,3E,3C,0,E,61,0,FE,7C
Data 33,FC,0,84,0,FF,86,6,3E,3A,FE,C,61,0,FE,6C
Data 33,FC,0,80,0,FF,86,6,3E,5,61,0,FE,5E,61,0
Data FE,70,60,0,FE,EA,2E,3A,FD,F2,61,0,FF,18,45,FA
Data FE,4,34,BC,0,1,33,FC,0,90,0,FF,86,6,33,FC
Data 1,90,0,FF,86,6,33,FC,0,90,0,FF,86,6,3E,3C
Data 0,E,61,0,FE,26,33,FC,0,80,0,FF,86,6,60,B8
Data 2E,3A,FD,B8,61,0,FE,DE,45,FA,FD,CA,34,BC,0,1
Data 33,FC,1,90,0,FF,86,6,33,FC,0,90,0,FF,86,6
Data 33,FC,1,90,0,FF,86,6,3E,3C,0,E,61,0,FD,EC
Data 33,FC,1,80,0,FF,86,6,60,0,FF,7E,2E,3A,FD,7C
Data 61,0,FE,A2,45,FA,FD,8E,34,BC,0,1,33,FC,1,90
Data 0,FF,86,6,33,FC,0,90,0,FF,86,6,33,FC,1,90
Data 0,FF,86,6,3E,3C,0,E,61,0,FD,B0,33,FC,1,84
Data 0,FF,86,6,3E,3A,FD,40,61,0,FD,A0,33,FC,1,80
Data 0,FF,86,6,60,0,FF,32,33,FC,0,80,0,FF,86,6
Data 61,0,FD,88,60,0,FD,9A,33,FC,0,86,0,FF,86,6
Data 3E,3A,FD,12,61,0,FD,74,33,FC,0,80,0,FF,86,6
Data 3E,5,61,0,FD,66,60,0,FD,78,2E,3A,FC,FE,26,7A
Data FC,FE,61,0,FE,20,33,FC,0,90,0,FF,86,6,33,FC
Data 1,90,0,FF,86,6,33,FC,0,90,0,FF,86,6,3E,3C
Data 0,1,61,0,FD,36,33,FC,0,80,0,FF,86,6,34,3A
Data FC,C8,3E,5,61,0,FD,24,61,0,FD,36,32,3A,FC,C4
Data 36,C1,51,CA,FF,EE,60,0,FD,A6,0,0
Data ***
'
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 45,FA,0,6,61,EE,60,CC,52,6F,75,74,69,6E,65,20
Data 6E,6F,63,68,20,6E,69,63,68,74,20,69,6D,706C,65
Data 6D,65,6E,74,69,65,72,74,2E,D,A,0,0
Data ***
Trackdaten:
Data 4E,2
' Trackvorspann
'
Data 00,2
' Sektorvorspann
Data F5,3,FE,1
' Syncbytes und Adreßmarke
Data Track,1,Side,1,1,1,3,1,F7,1
' Sektorvorspann und Checksumme
Data 4E,14,0,A
' Lückenbytes
Data F5,3,FB,1
' Syncbytes und Datenmarke
Data CB,400,F7,1
' Sektordaten und Checksumme
Data 4E,10
' Lückenbytes
'
Data 00,2
' Sektorvorspann
Data F5,3,FE,1
' Syncbytes und Adreßmarke
Data Track,1,Side,1,2,1,0,1,F7,l
' Sektorheader (Sektor mit 128 Bytes) und Checksumme
Data 4E,14,0,A
' Lückenbytes
Data F5,3,FB,1
' Syncs und Datenmarke
Data CB,40,F7,1
' Sektordaten (unvollständig) und Checksumme
Data 4E,10
' Lückenbytes
Data 00,2
' Sektorvorspann
Data FS,3,FE,1
Data Track,1,Side,1,3,1,2,1,F7,1
' normaler 512-Byte-Sektor
Data 4E,14,0,A
Data F5,3,FB,1
Data CB,200
' Sektordaten
Data F7,1,4E.10
'
Data 00,2
' Sektorvorspann
Data F5,3,FE,1
Data Track,1,Side,1,4,1,4,1,F7,1
' Sektor mit der Größenangabe '4‘
Data 4E,14,0,A
Data F5,3,FB,1
Data 02,200
' Sektordaten des zweiten Sektors Nr. 3
Data F7,1,4E,10
'
Data 4E,200
' Tracknachspann
Data ***,***
PRGTOLST.BAS ist ein klitzekleines Utility in GfA-Basic, das mit dem AS68 assemblierte (relokatible!) Programme in Datazeilen wandelt, die dann per ’MERGE’ in Ihr BASIC-Programm eingefügt werden können.
Beim Start fragt PRGTOLST in einer Fileselect-Box nach dem Namen der zu wandelnden Datei. Später müssen Sie noch die Größe der Datei eingeben, welche Sie aus dem Verzeichnis des Desktop erfahren (Textanzeige einschalten). PRGTOLST gibt zuerst alle Bytes der Datei,dann die Datazeilen auf dem Bildschirm an.Die Datazeilen werden gleichzeitig in eine Datei geschrieben, die den selben Namen wie die Originaldatei hat, zusätzlich aber die Extension LST besitzt. Diese Datei können Sie dann MERGEn.
Für den Experten: PRGTOLST.BAS schneidet den Programmheader eines fertig gelinkten Programms (das sind die ersten 28 Bytes) ab, weil er für ein vollständig relokatibles Programm, das in BASIC eingebunden wird, nicht notwendig ist.
Bei der Einbindung eines solchen Programmes geht man vor, indem man in BASIC einen genügend langen String definiert (etwa: PRGS = SPACE$(1000)), sich davon die Anfangsadresse geben läßt (Anfang = Varptr(prg$)) und daraufhin das Maschinenprogramm in einer Schleife in diesen String schreibt, (siehe auch die Routine Readprog im „TRACKMON“). Im „TRACKMON“ habe ich mich dafür entschieden, das Maschinenprogramm aus Data-Zeilen zu entnehmen, damit „Nur-BASIC-Programmierer“ die Chance haben, diesen Teil des MINIMON zu nutzen. Die so erzeugte Kopie des Maschinenprogramms im Speicher kann man schließlich mit Call (Anfang) aufrufen. In meinen Maschinenprogrammen bin ich aus Effizienzgründen davon abgekommen, die nötigen Parameter mit dem Call-Befehl übergeben zu lassen. Die Parameter werden in festgelegten Bereichen des Maschinencodes geschrieben (siehe Handhabung von LOCKSLEY.S und SELECT.S im „TRACKMON“, vor allem in der Routine Prntable) und auch von dort wieder abgeholt. (C.B.)
' Claus Brod
' PRGTO1.ST
' Konvertiert PKG-File zun. LST-File, das
' mit MERGE eingeladen werden Kenn
' Written 1987 by Claus Brod. Am Felsenkeller 2,
' 8772 Marktheidenfeld
'
'
Dim A%(5000) ! maximale Filegröße
Fileselect "\*.*","",F$
Print F$
Input "Größe der Datei in Bytes”;G ! Größe der Datei
S=Varptr(A%)
'
Bload F$,S ! Datei in Integerarray lesen
'
' Datei kurz ausgeben
'
Print "printing hex values of ";F$
For T=0 To G
P=Peek(T+S)
P$=Hex$(P)
Print Left$("00",2-Len(P$));PS’
If Inkey$=" "
Void Inp(2)
Endif
Next T
'
' Datei auf Diskette (aktuelles Verzeichnis)
' schreiben
'
Print
A=Instr(F$,".")
G$=Left$(F$,A)+"lst"
Print "Creating data lines for ";G$
Open "O",#1,G$
For T=28 To G Step 16
Print #1,"data ";
Print "data ";
For I=0 To 15
P$=Hex$(Peek(T+S+I))
Print #1,P$;
Print P$;
If InkeyS=" "
Void Inp(2)
Endif
If I<15
Print #1,".";
Print ".";
Endif
Next I
Print #1
Print
Next T
Close #1
Listing 2: PRGTOLS1.Bsc.
In der September-Ausgabe haben sich beim Montieren der Zeitung leider einige Fehler eingeschlichen. Wir bitten dies zu entschuldigen.
Im Listing von LOCKSLEY.S sind Zeilen verschluckt worden. Auf Seite 3 fehlt am Ende
move.w d1,(a2)
rts
Auf Seite 5 fehlt unten
move.w #$190,dmodus
move.w #14,d7