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
Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]