Floppyspielereien Teil 4

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.

MINIMON kräftig aufgeblasen

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 ***,***

WANDLUNGSFÄHIG

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.

# Der Fehlerteufel geht um!!!

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


Aus: ST-Computer 10 / 1987, Seite 115

Links

Copyright-Bestimmungen: siehe Über diese Seite