Trashcan ST - Endlich: Wiederverwertbarer Müll auch bei ST

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:

  1. 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.

  2. 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ß:

Bild 1: Cookiejar-Aufbau

Die Theorie

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.

Bild 2: Stack-Aufbau im Vergleich von 68000 und 68030

Die Implementation

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.

Bild 3: So könnte ein Desktop auf dem TT bzw. Mega STE aussehen.

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.

Optionen

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

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.

Aussichten

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

Friedel van Megen
Aus: ST-Computer 12 / 1991, Seite 112

Links

Copyright-Bestimmungen: siehe Über diese Seite