Es war einmal eine Computerfirma, die wollte einen neuen Computer vermarkten. Da dies schnell gehen sollte, versuchte man, Zeit zu sparen, wo immer es ging. Das führte dazu, daß einige Funktionen der Benutzeroberfläche, neudeutsch auch Desktop genannt, nicht durchdacht wurden.
So oder so ähnlich könnte ein Computermärchen beginnen. Aber leider war das (anscheinend) die Strategie von Atari bei der Einführung der ST-Modelle. Viele Dinge des Desktops sind nur deshalb nicht benutzerfreundlich, weil die Zeit fehlte, sie besser zu implementieren, nicht aber, weil das GEMDOS die benötigten Funktionen nicht bieten konnte.
Dazu gehört auch die Verwaltung des Papierkorbs auf dem Desktop. Wenn man einen Macintosh gewöhnt ist, ist der Doppelklick auf das Papierkorbsymbol nach dem versehentlichen Weg werfen einer Datei nur die logische Konsequenz aus der Illusion, einen richtigen Schreibtisch vor sich zu haben. Alle, die das nicht glauben, sollen mal nach den letzten weggeworfenen Listings im heimischen Papierkorb suchen. Derjenige, der das auf einem Atari ST versucht, erntet mit einem Doppelklick auf das Papierkorbsymbol nur eine hämische Dialogbox.
Das Desktop des Atari ist nicht in der Lage, sich eine weggeworfene Datei zu „merken“ und sie so für eventuelle Rettungsversuche zu konservieren. Einmal gelöscht, benötigt man Spezialprogramme und eine große Portion Glück, um zu retten, was noch zu retten ist. Diesen Umstand versucht die Gemini-Shell mit der Zweiteilung in Schredder und Papierkorb zu mildem. Dateien, die man später noch einmal retten will(...), wirft man in den Papierkorb, den man später durch das Werfen in den Schredder manuell leeren muß. Dabei ist der Papierkorb nichts anderes als ein bestimmter Ordner auf der Festplatte. Die Idee ist gar nicht schlecht, birgt aber zwei Nachteile:
Die Unterteilung in Papierkorb und Schredder ist natürlich nur auf dem Desktop wirksam. Wenn ein Programm eine Datei löscht, so ist diese auch weiterhin unwiederbringlich verloren.
Dateien, die im Papierkorb liegen, benötigen weiterhin Speicherplatz auf der Festplatte/Diskette. Wenn man erst innerhalb eines Programms merkt, daß der Platz auf dem Medium nicht reicht, kann man nur hoffen, daß das Programm eine Möglichkeit bietet, Dateien zu löschen, denn automatisch geschieht da nichts.
Dennoch, das ist der bisher einzige (mir bekannte) Ansatz, das Löschen „sicherer“ zu machen. Damit ist wohl klar, welche Eigenschaften ein Programm zur Papierkorbverwaltung erfüllen muß:
Zum einen muß eine automatische „Papierkorbverwaltung“ Dateien sichern, die aus einem beliebigen Programm heraus gelöscht werden. Zum anderen muß es den durch diese Dateien belegten Diskettenplatz automatisch wieder freigeben, wenn er benötigt wird.
Der erste Punkt wird durch eine Änderung des Fdelete-Aufrufs erreicht. Dieser muß gegen eine eigene Routine ausgetauscht werden. Diese Routine löscht eine Datei gleichen Namens im Papierkorb und renamed dann die zu löschende Datei in eben diesem.
Wenn das aus irgendeinem Grund nicht möglich ist, wird die Originaldatei gelöscht. Dies läßt sich verschmerzen, da das ohnehin die Absicht des Fdelete-Aufrufs war. Nebenbei bemerkt folgt aus der Tatsache, daß renamed wird, auch, daß auf jedem Laufwerk ein ‘Papierkorbordner’ existieren muß, da das GEMDOS Dateien nicht über logische Laufwerke hinweg umbenennen kann!Da es einige (wenige) Programme gibt, die vor einer Speicheroperation den freien Speicherplatz des Laufwerks überprüfen, muß die Dfree-Funktion ‘überarbeitet’ werden. Es wird nach dem normalen Aufruf noch die Länge der im Papierkorb liegenden Dateien ‘hinzugerechnet’. Dazu wird die Länge aller Dateien, die im TRASHDIR abgelegt sind, ermittelt, in Cluster umgerechnet und zur Anzahl der freien Cluster addiert.
Die Umleitung der Löschaktion ist aber nur die halbe Miete, denn auch beim Erzeugen einer Datei mittels Fcreate wird eine Datei mit gleichem Namen gnadenlos gelöscht. Aus diesem Grund muß auch der Fcreate-Aufruf dahingehend überarbeitet werden, daß eine gleichnamige Datei ins Trashdir wandert.
Dies führt aber zu einem Zwiespalt: Wenn man eine Datei im Papierkorb sichert, wenn sie mit Fcreate überschrieben werden würde, so könnte man keine Datei mehr aus dem Papierkorb zurückhoien. Jeder Versuch, diese Datei nach einer verunglückten Modifikation zu restaurieren, führt zu einem Fcreate-Aufruf. Dadurch würde man die zu restaurierende Datei endgültig vernichten, bevor man sie hätte retten können.
Diesem Dilemma geht mein Programm folgendermaßen aus dem Weg: Zum einen wird VOR dem Fcreate geprüft, ob eine Datei mit gleichem Namen existiert. Ist das nicht der Fall, wird auch die Datei im Papierkorb nicht verändert. Zum anderen verhindert das Festhalten der SHIFT-Taste eine Interpretation der Betriebssystemaufrufe durch TRASHCAN-ST. Es genügt dann, während des gesamten Kopier- bzw. Rename-Vorgangs die Shift-Taste gedrückt zu halten, um eine Änderung der Dateien im Trashdir zu verhindern.
Wer möchte, kann ja mal ‘Info anzeigen’ im Datei-Menü anwählen. Mit gedrückter Shift-Taste und gefülltem Trashdir-Ordner sollte die Anzahl der freien Bytes deutlich kleiner sein.
Die zweite Forderung wird durch eine Änderung der Fwrite-Funktion erreicht. Wenn der durch die Papierkorbdateien belegte Diskettenplatz wirklich benötigt wird, müssen diese Dateien zur rechten Zeit gelöscht werden. Das ist aber relativ einfach realisierbar. Es existiert nur eine einzige Funktion, die schreibt: Fwrite. Es genügt, einfach den Aufruf vom Fwrite intern durchzuführen und dann zu überprüfen, ob die Anzahl der zu schreibenden Bytes gleich der Anzahl der geschriebenen Bytes ist. Ist das der Fall, ist alles in Ordnung; andernfalls wird die älteste Datei im Papierkorb gelöscht und dann die Schreiboperation fortgesetzt, bis entweder alle Bytes geschrieben oder aber alle Dateien im Papierkorb gelöscht wurden. Dann ist wirklich kein Platz mehr frei...
Nur leider weiß keiner, auf welchem Laufwerk gerade Fwrite durchgeführt wird, da das GEMDOS an dieser Stelle nur mit einem Handle, nicht aber mit dem kompletten Pfadnamen arbeitet. Aus diesem Grund werden auch noch die Fcreate- und Fopen-Aufrufe abgefangen, um in einer Tabelle die zu den Handles gehörenden Laufwerksnamen zu speichern. Wenn Fclose aufgerufen wird, wird das Handle auch intern wieder freigegeben.
Das Programm versucht als erstes festzustellen, ob es bereits installiert war, um eine Doppelinstallation zu verhindern (die fatal wäre...). Dazu wird der GEMDOS-Vektor erfragt und mittels des XBRA-Verfahrens solange rückverfolgt, bis entweder die eigene Kennung gefunden wurde oder aber das Ende erreicht ist. Wer wissen möchte, wie das XBRA-Verfahren anzuwenden ist, der schaue sich das Listing an oder schmökere mal in [1].
Wird die eigene Kennung nicht gefunden, wird der Trap #1 - Vektor auf die eigene Routine umgebogen. Danach ermittelt das Programm den verwendeten Prozessortyp. Dazu wird eine Eigenschaft der neuen TOS-Versionen (ab TOS 1.6) ausgenutzt, das Cookiejar [2], Das Cookiejar ist nichts anderes als eine neue, von Atari dokumentierte Betriebssystemstruktur mit dem in Bild 1 beschriebenen Aufbau.
Es existiert eine neue Systemvariable, genannt _p_cookies, an der Adresse $5a0. Da dieser Speicherplatz auch bei allen früheren TOS-Versionen unbenutzt ist, kann man diese Struktur bei allen TOS-Versionen ‘nachrüsten’. _p_cookies ist dann ein Pointer auf eine Liste von 8 Byte großen Elementen.
Jedes Element besteht aus:
dc.l "MyID" ;Magic ID
dc.l $12345678 ;zugehörige Daten.,
dc.l $0 ;Ende der Liste
dc.l $6 ;maximal mögliche Anzahl
der Einträge (hier: Länge
in Bytes: 6*8!!)
Es interessiert aber nur das _CP(/-cookie, das vom TT-TOS angelegt wird. Im Datenteil ist der Prozessortyp codiert. Dabei steht eine 10 für den MC68010, eine 20 für den 68020 etc. Das ist wichtig, da der Stack-Aufbau einer Exception vom Prozessortyp abhängig ist. Ab dem MC68010 wird nämlich bei einer Exception ein zusätzliches Wort auf dem Stack abgelegt. Mit dessen Hilfe unterscheidet der Prozessor verschiedene Stack-Formate. Dieses zusätzliche Wort wird VOR den üblichen Daten (PC, Status) abgelegt und besitzt den folgenden Aufbau: %DDDD-FFFFFFFFFFFF.
Dabei codiert %FFFFFFFFFFFF die Vektornummer der auslösenden Exception. In %DDDD ist der Exception-Typ codiert. %0000 steht für eine ‘normale’ Exception, %1001 für einen Bus- bzw. Adreß-Error, bei dem 29 zusätzliche Wörter auf dem Stack abgelegt werden.
Da der Trap#1-Vektor ein normaler Exception-Vektor ist, wird nur das Zusatzwort gespeichert. Leider erhöht sich das Offset zu den auf dem Stack abgelegten Daten dadurch um 2 Byte, was der Grund für einige Inkompabilitäten zwischen ST und TT sein dürfte. So kommt der Traphandler nicht mehr an die Parameter, wenn man das GEMDOS aus dem Supervisormodus heraus aufruft. Meine neue GEMDOS-Routine beachtet diesen Unterschied natürlich, um so auf jedem Atari lauffähig zu sein.
Auch ist sie wiedereintrittsfähig, was aber nicht heißen soll, daß das jetzt auch für die Originalroutinen gilt. Das GEM-DOS ist nicht reentrant, aber da innerhalb des GEMDOS-Aufrufs das GEMDOS wiederum aufgerufen wird, sind die einzelnen Routinen durch Semaphoren gegen einen Wiedereintritt abgesichert. Eine Semaphore kann man sich als Schalter vorstellen, der eine Bearbeitung erlaubt oder verbietet. Dabei gilt es jedoch einige Spielregeln zu beachten. Das Abfragen und Setzen einer Semaphore muß ‘unteilbar’ sein, damit in Bild 2 illustrierter Zustand nicht auftreten kann.
Die Routine prüft ihre Semaphore und stellt fest, daß sie noch nicht gesetzt ist. Fein, denkt sie sich, aber während sie noch mit dem Freuen beschäftigt ist, setzt eine andere Routine (z.B. aus einem Interrupt heraus) eben diesen Schalter. Nachdem die Freude abgeklungen ist, setzt auch die ahnungslose Routine den (schon gesetzten) Schalter. Jetzt denken also beide Routinen, daß sie die exklusiven Zugriffsrechte auf die durch die Semaphore geschützten Daten haben, und legen los. Jeder male sich die Folgen aus...
Der MC68k-Befehlssatz bietet aber eine elegante Möglichkeit, dem oben beschriebenen Konflikt aus dem Weg zu gehen. Der TAS-Befehl testet die angegebene Speicherstelle, setzt die Prozessor-Flags entsprechend und schreibt dann $ff in diese Speicherstelle, ohne die Flags zu beeinflussen.
tas $adr ;Semaphore testen und belegen
bne ende ;war schon gesperrt...
clr.b $adr ;Semaphore freigeben
ende: ..
Im Gegensatz zur Lösung mittels
tst.b $adr ;Semaphore testen
bne ende ;<>0 bedeutet Ende
move.b #$ff,$adr ;Semaphore setzen..
clr.b $adr ;Semaphore freigeben
ende: .. ;hier geht's weiter
ist der erste Zyklus unteilbar. Davon wird bei allen kritischen Routinen Gebrauch gemacht.
Kommen wir jetzt zu den Informationen, die auch den Nur-Anwender interessieren werden. Auf allen Laufwerken bzw. Disketten, auf denen Trashdir wirken soll, muß ein Ordner mit dem Namen TRASHDIR erzeugt werden. So wird es möglich, bestimmte Laufwerke/Partitionen von der automatischen Sicherung auszunehmen.
Die Dateien werden, wenn sie in das Trashdir wandern, mit dem aktuellen Datum/Uhrzeit versehen. Dadurch hat man zum einen stets die Übersicht, wann man was gelöscht hat. Zum anderen wird es dadurch erst möglich, bei Bedarf die Dateien in der Reihenfolge ihrer Löschung endgültig zu eliminieren. Dateien, die schon im Papierkorb abgelegt sind, kann man durch nochmaliges Löschen endgültig wegwerfen. Wenn man eine Datei aus dem Papierkorb zurückholen will, muß man die Shift-Taste während der gesamten Kopier- bzw. Rename-Aktion gedrückt halten. Das ist zwar nicht unbedingt nötig, aber so vermeidet man Datenverlust in der letzten Sekunde. Zur Erklärung: Wenn man eine Datei innerhalb eines Laufwerks kopiert, benötigt sie natürlich eigenen Platz auf dem Medium. Sollte der Restplatz nicht reichen, kann es passieren, daß die zu rettende Datei [die ja im TRASHDIR liegt und somit ja unnütz(!) ist] gelöscht wird, bevor sie gerettet wurde. Wenn man während dieser Zeit aber die Shift-Taste festhält, w erden alle Abfragen der Mülleimer-Software umgangen (inkl. dem automatischen Löschen des Trashdirs bei Speicherplatzmangel auf dem Medium).
Beim Atari TT kann man die Trashdir-Ordner auf das Desktop ablegen. Man braucht eine Datei nur in den Originalpapierkorb zu werfen und kann es aus dem Trashdir des jeweiligen Laufwerks bequem herausholen (siehe auch Bild 3)
Probleme, die bei der Erprobung aufgefallen sind, sollen nicht unerwähnt bleiben:
Zum einen funktioniert das Programm nicht mit der komprimierenden R AM-Disk Maxidisk. Beim Schreiben auf das volle Laufwerk wird die zu schreibende Datei zerstört. Abhilfe: benutze eine andere RAM-Disk (schade).
Ein zweiter Fehler hängt mit dem TOS 1.04 zusammen. Dieses TOS kommt ins Schleudern, falls beim Umbenennen einer Datei in einen Ordner dieser erweitert werden müßte. Dieser Fall tritt nach einem Vielfachen von 32 Dateieinträgen ein (inklusive den Einträgen für aktuelles [.] und übergeordnetes [..] Directory). Wenn kein Platz auf dem Laufwerk mehr frei ist, der für den neuen Directory-Eintrag benutzt werden könnte, stürzt TOS 1.04 in diesem Fall einfach ab! Dieser Fall tritt zwar normalerweise nicht auf, regelmäßiges Leeren des Papierkorbs beugt dem aber dennoch sicher vor.
Den dritten Fehler begeht das Desktop des Atari TT (3.01 29.08.1990). Wenn es eine Datei auf ein Medium schreibt, auf das sie aber nicht vollständig paßt, so löscht es diese Datei, ohne sie vorher zu schließen. Da dies durch die Papierkorbverwaltung in einen Frename-Aufruf geändert wird, versucht das GEMDOS eine nicht geschlossene Datei umzubenennen. Durch diese Aktion kommt das GEMDOS so ins Schleudern, daß sowohl die Datei nicht mehr auffindbar ist, als auch der Platz auf dem Medium weiterhin belegt bleibt.
Für die, die alles verbessern wollen, noch eine Anregung: Das Löschen dauert umso länger, je mehr Dateien im Trashdir sind. Es wäre also ratsam, das Programm dahingehend zu erw eitern, daß nach dem Start alle Dateien in den Trashdirs, die älter als z.B. eine Woche sind, gelöschet werden. Man könnte auch Dateien mit einer bestimmten Endung von der Sicherung ausnehmen... Auch könnte man die Sicherung auf bestimmte Dateiendungen beschränken... Aber das hätte das Programm noch weiter aufgebläht, als dies ohnehin schon der Fall ist.
[1] Jankowski, Reschke, Rabich; ATARI ST Profibuch, Sybex-Verlag, ISBN 3-88745-563-0 12] Atari STE TOS release notes, 12. Jan. 1990
;********************************************
;** Mülleimer V1.1
;** Entwickelt mit MAS1.5
;** 1991 by Friedel van Megen
;** (c) 1991 MAXON Computer
;********************************************
Cconws EQU 9 ;Das bedarf wohl keiner Erklärung ..
Dgetdrv EQU 25
Dfree EQU 54
Tgetdate EQU 42
Tgettime EQU 44
Fdatime EQU 87
Fgetdta EQU 47
Fsetdta EQU 26
Fdelete EQU 65
Fwrite EQU 64
Frename EQU 86
Fsfirst EQU 78
Fsnext EQU 79
Fcreate EQU 60
Fopen EQU 61
Fclose EQU 62
Ptermres EQU 49
gemdos EQU 1
Setexec EQU 5
bios EQU 13
Supexec EQU 38
xbios EQU 14
;********************************************
;** Ab hier wird es interessant
;********************************************
TEXT
tr_start: pea myname
move.w #Cconws,-(sp)
trap #gemdos
addq.l #6,sp
move.l #-1,-(sp)
move.w #33,-(sp) ;gemdos-vector-number
move.w #Setexec,-(sp)
trap #bios
addq.l #8,sp
move.l d0,a0
in_test: cmp.l #0,a0
beq install ;Kettenende erreicht
cmp.l #'XBRA',-12(a0)
bne install
cmp.l #'FGEM',-8(a0) ;eigenen ID suchen beq no_inst
move.l -4(a0),a0 ;nächstes Kettenglied
bra in_test
no_inst: pea warschon ;das war wohl nichts
move.w #Cconws,-(sp)
trap #gemdos
addq.l #6,sp
clr -(sp)
trap #gemdos
install: pea testST
move.w #Supexec,-(sp)
trap #xbios
addq.l #6,sp
pea n_trpl ;GEMDOS patchen
move.w #33,-(sp) ;gemdos-vector-number
move.w #Setexec,-(sp)
trap #bios
addq.l #8,sp
move.l d0,sv_trp1 ;alten Vektor sichern
move.l 4(sp),a1 ;Basepage Pointer vom Stack
move.l #$100,a0
add.l 12(a1),a0
add.l 20(a1),a0
add.l 28(a1),a0
move.w #0,-(sp) ;wir bleiben resident!
move.l a0,-(sp) ;Programmlänge
move.w #Ptermres,-(sp)
trap #gemdos ;Und Schluß...
DATA
myname: dc.b "Mülleimer V1.1 (vom 11 Mai 1991)",10,13
dc.b "(P) 1991 Friedei van Megen",10,13,0
warschon: dc.b "Der Mülleimer war schon installiert",10,13,0
even
TEXT
;********************************************
;** Test auf den Prozessor
;********************************************
testST: move.l $5a0,d0 ;cookie-root-pointer
beq endST
move.l d0,a0
move.l #'_CPU',d0
testST1: move.l (a0)+,d1
beq endST
cmp.l d0,d1
beq found ;_CPU cookie gefunden
addq.l #4,a0
bra testST1
found: move.l (a0),d1
;Prozessortyp holen (0, 10, 20 steht für MC68000, MC68010 )
beq endST ;nur ein MC68000
add.w #2,stk_offset
endST: move.l $4f2,a0 ;Sysbase
move.w 2(a0),d0
cmp.w #$0100,d0
bne testST2
rts
testST2: move.l 36(a0),shift ;Pointer auf SHIFT-Status holen (ab TOS1.2)
rts
;************************************************
;** modifizierter TRAP #1 Handler, XBRA-tauglich
;************************************************
SUPER
dc.l 'XBRA'
dc.l 'FGEM'
sv_trp1: dc.l 0 ;savearea für gemdos vektor
n_trp1: tst.w sema ;darf ich was machen?
bne end_trp1 ;JA ->
move.l shift,a0 ;falls SHIFT gedrückt wurde: ABBRUCH
move.b (a0),d0
and.b #%11,d0
bne end_trp1
move.l a7,a0 ;Stackpointer bestimmen
move.w stk_offset,d0
lea 6(a0,d0.w),a0 ;Offsetänderung ab MC68010 ausgleichen
move.w (sp),d0 ;Statuspaket, das beim TRAP abgelegt wurde
btst #13,d0
bne in_supm ;ok, Supervisor
move.l usp, a0 ;Aufruf aus USER-Mode
in_supm: move.w (a0)+,d0 ;Funktionscode
cmp.w #Fdelete,d0 ;Fdelete?
beq myFdel
cmp.w #Fwrite,d0 ;Fwrite ?
beq myFwrite
cmp.w #Fclose,d0 ;Fclose
beq myFclose
cmp.w #Fcreate,d0 ;Fcreate
beq myFcreat
cmp.w #Fopen,d0 ;Fopen
beq myFopen
cmp.w #Dfree,d0 ;Dfree
beq myDfree
end_trp1: move.l sv_trp1,a0
jmp (a0) ;dann eben nicht...
USER
;***************************************************
;** neue Dfree-Routine, pointer auf Parameter in A0
;***************************************************
SUPER
myDfree: tas sema ;sicherstellen, daß wirklich niemand
beq dfree1 ;sonst dazwischen funkt
move.l sv_trp1,a0
jmp (a0)
dfree1: movem.l d3/d4/a3/a4,-(sp)
move.l a0,a3
move.l (a0),a4 ;Pointer auf DISKINFO
move.w #Fgetdta,-(sp) ;alten DTA sichern
bsr _gemdos
addq.l #2,sp
move.l d0,sv_dta
pea my_dta ;und eigenen setzen
move.w #Fsetdta,-(sp)
bsr _gemdos
addq.l #6,sp
move.w 4(a3),-(sp) ;Originalroutine ausführen
move.l (a3),-(sp)
move.w #Dfree,-(sp)
bsr _gemdos
addq.l #8, sp
move.l d0,-(sp) ;Status DFREE Sichern
tst.w d0
bmi endDfree
lea trashname,a0 ;ist Laufwerk angegeben
move.b #'*',0(a0)
move.b #'.',1(a0)
move.b #'*',2(a0)
clr.b 3(a0)
lea trashdir,a0
tst.w 4(a3)
beq dfree2
move.b 5(a3),trashdrive
add.b #'A' - 1,trashdrive ;Laufwerk eintragen
lea trashdrive,a0
dfree2: move.w #%100110,-(sp) ;WRI/SYS/HID-Bits
move.l a0,-(sp)
move.w #Fsfirst,-(sp)
bsr _gemdos
addq.l #8,sp
tst.l d0
bmi endDfree ;Keine Datei, bzw. kein Ordner vorhanden
lea my_dta,a3
move.l 8(a4),d4
mulu.w 14(a4),d4 ;Anzahl der Bytes pro cluster
tst.w d4
beq endDfree ;Unsinn im Parameterblock
move.l 26(a3),d3 ;Programmlänge
add.l d4,d3 ;in Cluster umrechnen
subq.l #1,d3
divu d4,d3
and.l #$ffff,d3
dfree3: move.w #Fsnext,-(sp)
bsr _gemdos
addq.l #2,sp
tst.l d0
bmi dfree4 ;Ende der Verzeichniseinträge erreicht
move.l 26(a3),d0 ;Programmlänge
add.l d4,d0
subq.l #1,d0
divu d4,d0
and.l #$ffff,d0 ;Der Rest interessiert nicht
add.l d0,d3 ;und addieren...
bra dfree3
dfree4: add.l d3,0(a4) ;es ist noch mehr frei als Du denktst...
endDfree: move.l sv_dta,-(sp) ;a1ten DTA restaurieren
move.w #Fsetdta,-(sp)
bsr _gemdos
addq.l #6,sp
move.l (sp)+,d0 ;Status DFREE restaurieren
movem.l (sp)+,d3/d4/a3/a4
clr.w sema
rte
USER
;***************************************************
;** neue Fclose-routine, pointer auf parameter in A0
;***************************************************
SUPER
myFclose: tas sema ;sicherstellen, daß wirklich niemand
beq fclose1 ;sonst dazwischen funkt
move.l sv_trp1,a0
jmp (a0)
fclose1: move.w (a0),-(sp) ;sichern
move.w (a0),-(sp)
move.w #Fclose,-(sp)
bsr _gemdos
addq.l #4,sp
tst.l d0
bmi endclose
lea hdl_tab,a0
move.l d1,-(sp) ;d1 sichern
move.w 4(sp),d1
bmi fclose2
clr.b 0(a0,d1.w) ;Eintrag löschen
fclose2: move.l (sp)+,d1
endclose: move.w (sp)+,a0
clr.w sema
rte
USER
;***************************************************
;** neue Fcreate-routine; pointer auf parameter in A0
;***************************************************
SUPER
myFcreat: move.w #Fcreate,d0
tas sema ;sicherstellen, daß wirklich niemand
beq fcrea1 ;sonst dazwischen funkt
move.l sv_trp1,a0
jmp (a0)
fcrea1: movem.l a1-a3/d1,-(sp)
move.w d0,-(sp) ;Opcode retten!
move.l a0,a1 ;Sichern vom Pointer auf Pointer auf den Dateinamen
move.w #Dgetdrv,-(sp) ;aktuelles Laufwerk bestimmen
bsr _gemdos
addq.l #2,sp
add.w #'A',d0 ;Laufwerksname
move.l d0,d1
move.l (a1),a0 ;Pointer auf Dateiname
move.b 1(a0),d0 ;Pfad prüfen
cmp.b #':',d0 ;ex Laufwerksangabe?
bne fcrea2 ;NEIN ->
move.b (a0),d1 ;sonst übernehmen
fcrea2: move.w (sp),d0 ;NICHT (sp)+ !
cmp.w #Fcreate,d0
bne fcrea3 ;nur bei Fcreate
move.b d1,trashdrive ;Laufwerk eintragen
move.l a0,a2 ;Default
fcrea21: move.b (a0)+,d0 ;Prograramnamen suchen
beq fcrea23 ;Ende erreicht
cmp.b #'\', d0
beq fcrea22 ;'\', oder ':' sind "Trenner"
cmp.b #':',d0
bne fcrea21
fcrea22: move.l a0,a2 ;Pointer merken
bra fcrea21
fcrea23: lea trashname,a0 ;Programmnamen übertragen
fcrea24: move.b (a2)+,(a0)
tst.b (a0)+
bne fcrea24
move.l (a1),a0 ;Pointer auf Dateinamen
bsr fexist
tst.l d0
bmi fcrea25 ;nur löschen, wenn anzulegende Datei ex.
move.l #trashdrive,-(sp) ;Duplikat im Mulleimer löschen
move.w #Fdelete,-(sp)
bsr _gemdos
addq.l #6,sp
fcrea25: move.l #trashdrive,-(sp) ;Datei in den Müll schieben
move.l (a1),-(sp) ;Pointer auf Dateiname
clr.w -(sp)
move.w #Frename,-(sp)
bsr _gemdos
lea 12(sp),sp
tst.l d0
bne fcrea3 ;Fehler beim renamen
move.l #trashdrive,a3
bsr tim_upd ;Zeit updaten
fcrea3: move.w (sp),d0 ;Opcode zurückholen
move.w 4(a1),-(sp)
move.l (a1),-(sp)
move.w d0,-(sp) ;Fcreate/Fopen ist identisch
bsr _gemdos
addq.l #8,sp
tst.w d0 ;nur positive Handles zulassen
bpi fcrea4 ;kein Fehler beim öffnen der Datei
cmp.l #-36,d0 ;Kein Directoryplatz mehr frei
bne endcreat
bsr trashclr ;Datei löschen
tst.l d0
beq endcreat ;Trashcan war leer
move.w (sp),d0 ;Opcode zurückholen
move.w 4(a1),-(sp)
move.l (a1),-(sp)
move.w d0,-(sp) ;Fcreate/Fopen ist identisch
bsr _gemdos
addq.l #8,sp
tst.w d0 ;Fehler beim Öffnen
bmi endcreat
fcrea4: lea hdl_tab,a0
move.b d1,0(a0,d0.w) ;Laufwerk eintragen
endcreat: addq.l #2,sp ;Stackkorektur
movem.l (sp)+,a1-a3/d1
clr.w sema
rte
USER
;********************************************************
;** Gemdos aufrufen (anspringen mittels bsr _gemdos !!!)
;********************************************************
SUPER
_gemdos: move.w sr,d0 ;Prozessor Exception vorgaukeln
tst.w stk_offset
beq _gemdos1
move.l (sp)+,a0
move.w #33,-(sp) ;Zusätzliches Wort ab MC68010 (siehe Text)
move.l a0,-(sp)
_gemdos1: move.w d0,-(sp)
move.l sv_trp1,a0
jmp (a0) ;GEMDOS aufrufen
USER
;********************************************************
;** Test auf Existenz einer Datei, Eingabe Pointer auf Dateiname(A0)
;** Ausgabe <0 Datei ex. NICHT
;********************************************************
fexist: movem.l d3/a1,-(sp)
clr.l d3 ;Fehler (default)
move.l a0,a1
move.w #Fgetdta,-(sp) ;alten DTA sichern
bsr _gemdos
addq.l #2,sp
move.l d0,sv_dta
pea my_dta ;und eigenen setzen
move.w #Fsetdta,-(sp)
bsr _gemdos
addq.l #6,sp
move.w #%100110,-(sp) ;WRI/SYS/HID-Bits
move.l a1,-(sp)
move.w #Fsfirst,-(sp)
bsr _gemdos
addq.l #8,sp
move.l d0,d3 ;status merken
tst.l d0
bmi endex ;Die Datei ex. nicht
moveq.l #0,d3
endex: move.l sv_dta,-(sp) ;alten DTA restaurieren
move.w #Fsetdta,-(sp)
bsr _gemdos
addq.l #6,sp
move.l d3,d0 ;Status zurückgeben
movem.l (sp)+,d3/a1
rts
;********************************************************
;** Zeit/Datum einer Datei updaten Eingabe A3
;********************************************************
tim_upd: move.w #0,-(sp) ;Versuche die Datei im Müll zu öffnen
move.l a3,-(sp)
move.w #Fopen,-(sp)
bsr _gemdos
addq.l #8,sp
tst.l d0
bmi end_upd ;hat nicht geklappt
move.w d0,-(sp) ;Fclose vorbereiten
move.w #Fclose,-(sp)
move.w #1,-(sp) ;Tdatime vorbereiten
move.w d0,-(sp)
pea timeptr
move.w #Fdatime,-(sp)
move.w #Tgettime,-(sp) ;Zeit holen
bsr _gemdos
addq.l #2,sp
move.w d0,timeptr
move.w #Tgetdate,-(sp) ;Datum holen
bsr _gemdos
addq.l #2,sp
move.w d0,timeptr+2
bsr _gemdos ;Fdatime
lea 10(sp),sp
bsr _gemdos ;Fclose
addq.l #4,sp
end_upd: rts
;********************************************************
;** neue Fopen-routine, pointer auf parameter in A0 ;********************************************************
myFopen: move.w #Fopen,d0
tas sema ;sicherstellen, daß wirklich niemand
beq fcrea1 ;sonst dazwischen funkt
move.l sv_trp1,a0
jmp (a0)
;********************************************************
;** neue Fdelete-routine; pointer auf parameter in A0
;********************************************************
SUPER
myFdel: tas sema ;sicherstellen, daß wirklich niemand
beq fdel1 ;sonst dazwischen funkt
fdelerr: move.l sv_trp1,a0
jmp (a0)
fdel1: movem.l a1-a4,-(sp)
move.l (a0),a4
move.l (a0),a1 ;Pointer auf zu löschenden Programmnamen
move.l a1,a2 ;Pointer sichern...
move.b 1(a1),d0
clr.b trashdrive ;keine Laufwerksangabe (default benutzen)
cmp.b #':',d0
bne fdel2
move.b (a1),trashdrive ;Laufwerksnamen eintragen
fdel2: move.b (a1)+,d0 ;Programmnamen suchen
beq fdel4 ;Ende erreicht
cmp.b #'\',d0
beq fdel3 ;'\' oder ':' sind "Trenner"
cmp.b #':',d0
bne fdel2
fdel3: move.l a1,a2 ;Pointer merken
bra fdel2
fdel4: lea trashname,a1 ;Programmnamen übertragen
fdel41: move.b (a2)+,(a1)
tst.b (a1)+
bne fdel41
lea trashdrive,a3 ;Wenn eine Laufwerksangabe vorhanden, benutzen
tst.b trashdrive
bne fdel5
lea trashdir,a3
fdel5: move.l a3,-(sp) ;Duplikat im Mülleimer löschen
move.w #Fdelete,-(sp)
bsr _gemdos
addq.l #6,sp
move.l d0,-(sp) ;Status merken
move.l a3,-(sp) ;Datei in den Müll schieben
move.l a4,-(sp)
clr.w -(sp)
move.w #Frename,-(sp)
bsr _gemdos
lea 12(sp),sp
tst.l d0
bne fdel6
bsr tim_upd ;Zeit updaten
bra fdel_end
fdel6: cmp.l #-34,d0 ;Ordner nicht gefunden
bne fdel_end ;alles klar
move.l a4,-(sp) ;Dann eben das Original löschen
move.w #Fdelete,-(sp)
bsr _gemdos
addq.l #6,sp
fdel_end: tst.l d0
bmi fdel_e2 ;Fehler beim löschen
addq.l #4,sp ;Status vergessen
bra fdel_e4
fdel_e2: tst.l (sp) ;konnte denn die Datei im Müll gelöscht werden?
bmi fdel_e3 ;NEIN ->
move.l (sp)+,d0 ;alten Status nehmen
bra fdel_e4
fdel_e3: addq.l #4,sp ;nicht Müll und nicht vorhanden
fdel_e4: movem.l (sp)+.a1-a4
clr.w sema ;Semaphore freigeben
rte
USER
DATA
timeptr: dc.w 0,0,0,0
TEXT
;********************************************************
;** neue Fwrite-routine
;********************************************************
SUPER
myFwrite: tas sema ;sicherstellen, daß wirklich niemand
beq fwri1 ;sonst dazwischen funkt
move.l sv_trp1,a0
jmp (a0)
fwri1: movem.l a3/a4/d3,-(sp)
move.l 2(a0),d3 ;COUNT
move.l 6(a0).a3 ;BUFFER
move.l a0,a4 ;Pointer auf Paramter
fwri2: move.l a3,-(sp) ;erst versuchen alles normal zu schreiben
move.l d3,-(sp)
move.w (a4),-(sp)
move.w #Fwrite,-(sp)
bsr _gemdos
lea 12(sp),sp
tst.l d0
bmi fwri_end ;Fehler, den ich NICHT beheben kann...
cmp.l d0,d3
bne fwri3 ;Platz hat noch nicht gereicht
move.l 2(a4),d0
bra fwri_end ;Zurückgeben der geschriebenen Bytes
fwri3: add.l d0,a3
sub.l d0,d3 ;der Rest muß noch geschrieben werden
bsr trashclr
tst.l d0
bne fwri2 ; Der Trashcan war noch nicht leer
move.l 2(a4),d0
sub.l d3,d0 ;Wahre Anzahl geschriebener Bytes berechnen
fwri_end: movem.l (sp)+,a3/a4/d3
clr.w sema ;Semaphore freigeben
rte
USER
trashclr: movem.l d1/d3/a1-a3,-(sp)
clr.l d3 ;Fehler (default)
move.w #Fgetdta,-(sp) ;alten DTA sichern
bsr _gemdos
addq.l #2,sp
move.l d0,sv_dta
pea my_dta ;und eigenen setzen
move.w #Fsetdta,-(sp)
bsr _gemdos
addq.l #6,sp
lea hdl_tab,a0 ;Laufwerk holen
clr.b trashdrive
move.w (a4),d0 ;Handlenummer holen
bmi trash1
move.b 0(a0,d0.w),trashdrive
trash1: lea trashdrive,a0 ;Laufwerksbezeichnung vorhanden?
tst.b trashdrive
bne trash2 ;JA ->
lea trashdir,a0
trash2: lea trashname,a1 ;nach löschbaren Dateien suchen
move.b #'*',(a1)
move.b #'.',1(a1)
move.b #'*',2(a1)
clr.b 3(a1)
move.w #%100110,-(sp) ;WRI/SYS/HID-Bits
move.l a0.-(sp)
move.w #Fsfirst,-(sp)
bsr _gemdos
addq.l #8,sp
tst.l d0
bmi endtrash ;Keine Datei, bzw. kein Ordner vorhanden
lea my_dta,a3
lea tname,a0
lea 30(a3),a2 ;Pointer auf Dateinamen
moveq.l #14,d0
tloop1: move.b (a2)+,(a0)+
dbra d0,tloop1 ;Dateiname übertragen
move.w 24(a3),tdate ;älteste Datei suchen (Pointer auf Datum)
move.w 22(a3),ttime
trash3: move.w #Fsnext,-(sp)
bsr _gemdos
addq.l #2,sp
tst.l d0
bmi trash4 ;Ende der Verzeichniseinträge erreicht
move.l 22(a3),d0 ;Alter vergleichen
swap d0 ;Zeit/Datum vertauschen
move.l tdate,d1
cmp.l d1,d0
bpl trash3 ;die neue Datei ist wohl älter
lea tname,a0 ;Die Datei ist wirklich älter...
lea 30(a3),a2 ;Pointer auf Dateinamen
moveq.l #14,d0
tloop2: move.b (a2)+,(a0)+
dbra d0,tloop2 ;Dateiname übertragen
move.w 24(a3),tdate
move.w 22(a3),ttime
bra trash3
trash4: lea tname,a0
lea trashname,a2 ;Pointer auf Dateinamen
moveq.l #14,d0
tloop3: move.b (a0)+,(a2)+
dbra d0,tloop3 ;Dateiname übertragen
lea trashdrive,a0 ;Laufwerksbezeichnung vorhanden?
tst.b trashdrive
bne trash5 ;JA ->
lea trashdir,a0
trash5: move.l a0,-(sp)
move.w #Fdelete,-(sp)
bsr _gemdos
addq.l #6,sp
tst.l d0 ;Fehler beim Löschen9?
bmi endtrash ;JA ->
moveq.l #1,d3 ;älteste Datei gelöscht
endtrash: move.l sv_dta,-(sp) ;alten DTA restaurieren
move.w #Fsetdta,-(sp)
bsr _gemdos
addq.l #6,sp
move.l d3,d0 ;Status zurückgeben
movem.l (sp)+,d1/d3/a1-a3
rts
DATA
tdate: dc.w 0
ttime: dc.w 0
tname: ds.b 20
TEXT
;*******************************************
;** datasegment
;******************************************
DATA
sema: dc.w 0 ;eine Semaphore
shift: dc.l $e1b ;Shift-Status (Hier noch fur TOS1.0)
stk_offset: dc.w 0 ;Stackoffset beim MC68000: 0, (ab 68010 2)
sv_dta: dc.l 0
my_dta: ds.b 64 ;Puffer für den DTA
trashdrive: dc.b "X:"
trashdir: dc.b "\TRASHDIR\"
trashname: ds.b 18
hdl_tab: ds.b 128 ;Zuordnung handle <-> Laufwerk
END