Bei allen Vorteilen des Desktops - einen Nachteil hat es bestimmt: ein Diskettenwechsel wird nicht gemerkt! Man muß stattdessen immer durch einen Druck auf die ESC-Taste manuell nachhelfen. Das ist mir (wie sicherlich schon vielen anderen) allmählich auf die Nerven gegangen, so daß ich kurzerhand ein kleines Programm geschrieben habe, das dem Floppy auf die Sprünge hilft.
Zuerst muß man sich dazu die Hardware des ST betrachten. Da gibt es zunächst einen sogenannten Floppy-Disk-Controller (FDC), einen selbständigen kleinen Prozessor, der nichts anderes macht, als Daten von einer angeschlossenen Floppy in den Arbeitsspeicher des ST zu übertragen (READ) oder umgekehrt (WRITE). Dieser Chip ist über 14 Leitungen mit der Floppy verbunden. Wie man aus Quelle (1) entnehmen kann, sind die Floppies aber imstande weit mehr zu leisten, als beim ATARI ST verlangt wird: Die sind nämlich mit sage und schreibe 34 (!!!) Anschlüssen ausgerüstet, nach Adam Riese liegen also 20 brach. Daß da auch mancher nützliche dem Rotstift zum Opfer fiel, liegt auf der Hand, so z.B. der Ready-Pin: er sollte durch ein LOW signalisieren, daß gerade eine Disk eingelegt wurde ...
„Wo die Hardware fehlt, kommt auch die beste Software nicht weiter“, dachten sich wohl die „cleveren“ Programmierer des Desktops und bauten eine Diskwechsel-Erkennungsroutine vorsichtshalber gar nicht erst ein. Daß es aber (meistens) doch geht, zeigt das vorliegende Programm. Bit 6 des Statusregisters des FDC enthält nämlich die interessante Information, ob die Disk im vorher selektierten Laufwerk schreibgeschützt ist oder nicht. Dazu muß man folgende drei Möglichkeiten betrachten:
Wenn man nun eine Nicht-WP-Disk einlegt, ändert sich das ominöse Bit: es wird gelöscht. Heißt: immer, wenn das Bit gelöscht wurde, ist gerade eine Diskette eingeschoben worden. Überwacht man dieses Bit in regelmäßigen Abständen (z.B. im Vertical Blank Interrupt), kann man jederzeit feststellen, ob ein Disk-Wechsel stattgefunden hat. Schön und gut: das klappt natürlich nur, wenn die eingeschobene Disk nicht schreibgeschützt ist. Aber ich denke, kaum einer wird nur mit schreibgeschützten Disketten arbeiten.
Braucht man also nur noch eine entsprechende VBL-Routine zu installieren, und schon ist man fertig, wie? Pustekuchen! Jetzt fängt die Arbeit ja erst an!
Dem Desktop muß gesagt werden, daß es das Inhaltsverzeichnis aktualisieren soll. Das ist ja noch ganz einfach, dazu braucht man ja nur einen ESC-Code in den Tastaturpuffer zu schreiben. Wie das gemacht wird, kann man z.B. in der ST-COMPUTER 4/91 nachlesen, deshalb will ich mich an diesem Punkt nicht lange aufhalten. Nur noch soviel: wer noch das Rauchpilz-TOS besitzt (das wird sicherlich kaum noch einen betreffen), muß zwei Programmzeilen ändern, sonst läuft das Programm bei ihm nicht. Zu diesen Ur-Zeiten bestand nämlich ein Eintrag im Tastaturpuffer nur aus einem Word (High-Byte: Scan-, Low-Byte: ASCII-Code). Diese wohl kaum bekannte Tatsache dürfte auch der Grund sein, warum etliche Programme nicht auf diesem System laufen...
Daraus ergibt sich aber gleich das Problem eines Programmstarts. Sobald man sich nicht mehr im Desktop befindet, wird ein ESC nicht mehr so verstanden, wie wir es wollen. Es würde vermutlich eher stören. Sobald also ein Programm gestartet wird, darf nichts mehr von unserer Seite in den Tastaturpuffer hinein. So weit, so gut. Wie überwacht man aber, wann ein Programm gestartet, und wann es beendet wird?
Dazu muß man sich schon ins GEMDOS hängen, das (Assembler-Programmierern wohlbekannt) über einen TRAP #1 aufgerufen wird. Vorher wurden die benötigten Parameter und zuletzt die Funktionsnummer auf dem Stapel abgelegt. Wir müssen jetzt folgendes tun:
Wichtig ist aber auch der Exec-Modus; nicht immer wird gleich ein Programm gestartet, wenn GEMDOS #75 aufgerufen wird! Außerdem gibt es Programme, die nie beendet werden. Zum einen gibt es da die ACCESSORIES, erkennbar an ihrem Namens-Anhängsel „ACC“. Bei ihnen darf der Zähler natürlich nicht erhöht werden, denn sie laufen ja quasi parallel zum Desktop. Und zum anderen handelt es sich um das Desktop selbst, das auch durch ein Pexec() gestartet wird! Es gibt jedoch nur eine Möglichkeit, daß unser Programm vor dem Desktop gestartet wurde, und zwar aus dem AUTO-Ordner heraus. Zu diesem Zeitpunkt ist aber auch noch nicht die Maus initialisiert, sprich der erste Slot in der VBL-Queue ist noch frei, denn der wird später immer durch die Mausbewegungsroutine belegt. Somit braucht man nur zu prüfen, ob der Slot frei ist, und schon weiß man Bescheid. In einem solchen Fall ist unser Zähler bei Programmstart auf -1 zu setzen, damit er dann beim Start des Desktops auf Null erhöht wird. Ab MC 68010 ist der Aufbau des Stacks bei einem TRAP anders. Daher muß man unter Berücksichtigung des Prozessortyps auf dem Stack die Parameter suchen. Einzelheiten hierzu entnehme man der ST-Computer 12/91 (Artikel: Trap-Trapper).
Wichtiger Hinweis zum Abtippen: Nach Ende der meisten Programme wird das Inhaltsverzeichnis automatisch aktualisiert. Q-DISK macht das bei Bedarf auch noch, was manchen evtl, nerven könnte. Deshalb habe ich die entsprechende Zeile (321) rechts mit „***!!! ***“ gekennzeichnet: hier wird das oben geschilderte Phänomen unterbunden. Wer aber auf Nummer Sicher gehen will, sollte diese Zeile einfach nicht mit abtippen.
Es versteht sich nach all diesen Erläuterungen wohl fast von selbst, daß ein solches Programm nur in Assembler zu realisieren ist. Ich denke aber, daß es dafür nicht zu lang geworden und trotzdem recht komfortabel ist. Einmal abgetippt und übersetzt, kopiert man es sich am besten gleich in den AUTO-Ordner und macht einen Reset. Das Programm bleibt dann resident im Speicher und verbraucht hier ca. 4,5 KB. Sollte man es versehentlich nochmal starten, wird es nach einer kurzen Meldung sofort wieder beendet, ohne weiteren Speicher zu belegen. Selbstverständlich kann es aber auch sonst jederzeit gestartet werden. Bei Verbiegung des TRAP #1 -Vektors unterstützt es die XBRA-Methode.
Meiner Ansicht nach müßte Q-DISK fast zu jedem anderen Programm laufen, ohne dieses zu stören oder zu verlangsamen. Bei den meisten Programmen reicht es aus, nur die Register d3-d7 und a3-a6 zu retten, denn laut (1) muß man damit rechnen, daß die anderen Register verändert werden. Es hat sich aber gezeigt, daß sich mancher Programmierer darüber hinweggesetzt hat, deshalb werden auch noch d1-d2 und a0-a2 gerettet. (d0 wird grundsätzlich geändert.) Das also gleich schon mal als Vorwarnung, bei mir traten aber bei keinem der getesteten Programme mehr Fehler oder gar Bomben auf. Auch die meisten vektorverbiegenden Hintergrundprogramme liefen fehlerfrei.
Natürlich ist das Programm erweiterbar. Man könnte es z.B. als Accessory starten und von ihm aus das Desktop manipulieren (Disk-Icons nach Disk-Namen beschriften, nur die angemeldeten Disks auch anzeigen usw.). Den fleißigen Programmierern ist da Tür und Tor geöffnet.
Literatur:
[1] H.-D. Jankowski, J. F. Reschke, D. Rabich: ATARI ST Profibuch, Sybex-Verlag, 2. Auflage 1989.
[2] ST-Computer, Nr. 4/91 und 12/91
;*********************************************
; Q-DISK V 1.3 *
;*********************************************
; Programm sorgt dafür, daß Diskettenwechsel *
; im Desktop erkannt werden, sofern die Disk *
; nicht schreibgeschützt ist. *
;*********************************************
; by Martin Glodde (c) 1992 MAXON Computer *
;*********************************************
; Offsets für Texte
info equ 0 ;Titelinformation
err equ 4 ;Text bei Installierungsfehler
taste equ 8 ;'Bitte Taste drücken'
installed equ 12 ;'Schon installiert'
; Offsets für Variablen
nflops equ 0 ;Anz. der Floppies
defdrv equ 2 ;Standardlaufwerk
keyparam equ 4 ;Tastaturpuffer-Parameterblock
WP equ 8 ;Schreibschutz-Flags
nprgs equ 10 ;Anz. gestarteter Programme
akt equ 12 ;Flags zur Akt. der Directories
save_area equ 14 ;Zeiger auf Bereich zur Registerrettung bei TRAP #1
autostart equ 18 ;Flag: Ist aus Autoordner gestartet worden ?
stk_offs equ 20 ;wo liegen Params bei TRAP#1? (abh. vom Prozessor)
;
prg:
movea.l 4(sp),a5 ;Zeiger auf Basepage
move.l $c(a5),d0 ;Länge des Textsegments
add.l $14(a5),d0 ;+ Länge des DATA-Segments
add.l $1c(a5),d0 ;+ Länge des BSS-Segments
addi.1 #$1100,d0 ;+ Länge der Basepage + 1K für Stack & Savearea
move.l d0,d1
add.l a5,d1
andi.l #-2,d1 ;Adresse des Stacks berechnen, runden ...
movea.l d1,sp ;...und setzen
lea.l varfeld(pc),a5 ; Zeiger auf Variablenfeld
move.l sp,eave.area(a5) ; Stackbereich dient später als Save Area
;
clr.w -(sp) ;kein Fehler
move.l d0,-(sp) ;Bedarf (für Ptermres)
move.w #$31,-(sp) ;Ptermres()
;
.DC.w $a00a ;Maus ausschalten
lea.l texte(pc),a6 ;Texttabelle
move.l info(a6),-(sp) ;1. Text ausgeben
move.w #9,-(sp) ;Cconwsl)
trap #1 ;GEMDOS
addq.l #6,sp
;
clr.l -(sp) ;Supervisor ein
move.w #32,-(sp) ;Super()
trap #1 ;GEMDOS
addq.l #6,sp
move.l d0,-(sp) ;alten Wert merken
bsr get_mc_type ;Prozessortyp holen
; (ab MC 68010 gibt's bei Exceptions mind. einen Parameter mehr)
move.w $4a6,d0 ;merken, wieviele Floppies existieren
subq.w #1,d0
move.w d0,nflops(a5)
move.w #25,-(sp) ;Dgetdrv()
trap #1 ;GEMDOS
addq.l #2,sp
move.w d0,defdrv(a5) ;Default-Drive merken
bsr get_wp ;Feststellen, ob Disk in A: oder B: schreibgeschützt ist
;
move.w #1,-(sp) ;Tastaturpuffer-Parameterblock
move.w #14,-(sp) ;Iorec()
trap #14 ,-XBIOS
addq.l #4, sp
move.l d0,keyparam(a5) ;Zeiger auf Parameterblock merken
;
lea.l newtrap(pc),a3 ;neuer TRAP #1-Vektor
move.l $84,-4(a3) ;alten Vektor in XBRA-Struktur eintragen
move.l a3,$84 ;neuen Vektor setzen
;
movea.l $456,a1 ;VBL-Queue
move.w $454,d6 ;_nvbls
subq.w #2,d6 ;-1 wg. dbra; -1 wg. reserviertem 1. Eintrag
tst.l 0(a1) ;Ist 1.Slot noch frei ?
seq autostart(a5) ;dann wurde aus AUTO-Ordner aufgerufen !
moveq.l #0,d1
vbl_lp:
tst.l 4(a1,d1.w) ;nach freiem Eintrag suchen
beq.s gefunden
movea.l 4(a1,d1.w),a0
cmpi.w #$4d47,-2(a0) ;eigene Kennung (d.h. schon installiert ?)
beq.s schon_inst ;ja, Abbruch
addq.w #4,d1 ;nächster Slot
dbra d6,vbl_lp
;
;Fehler bei Installierung:
movea.l err(a6),a4 ;Fehlertext
bra.s w2
schon_inst:
movea.l installed(a6),a4 ;sonst anderen Text nehmen
w2:
move.l -4(a3),$84 ;alten TRAP #1-Vektor wieder herstellen
move.w #32,-(sp) ;Super()
trap #1 ;GEMDOS (zurück in Usermodus)
lea.l $e(sp),sp ;Stack aufraumen: 6 für GEMDOS #32, 8 für
; nicht benötigte Parameter für Ptermres()
move.w #-1,-(sp) ;Prg.-Ende vorbereiten: Rückgabewert = Fehler!
move.w #76,-(sp) ;Pterm()
;
move.l a4,-(sp)
move.w #9,-(sp) ;Fehlermeldung ausgeben
trap #1
addq.l #6,sp
bra.s ok
;
gefunden:
move.l #vblrout,4(a1,d1.w) ;sonst VBL-Routine installieren
move.w #32,-(sp)
trap #1 ;wie oben zurück in den Usermodus
addq.l #6,sp ;aber Ptermres() lassen
;
ok:
tst.w autostart(a5)
bne.a auto ;bei Aufruf aus Autoordner nicht auf Taste warten
move.l taste(a6),-(sp) ;'Bitte Taste drücken' ausgeben
move.w #9,-(sp) ;Cconws()
trap #1 ;GEMDOS
addq.l #6,sp
move.w #7,-(sp) ;Crawcin()
trap #1 ;GEMDOS
addq.l #2,sp
bra.s ende
auto:
subq.w #1,nprgs(a5) ;weil bei AUTO-Start noch das Desktop initiali-
;siert (= mit Pexec() gestartet) wird ...
ende:
trap #1 ;Programmende (bei Fehler durch Pterm(),
;sonst durch Ptermres() )
;
get_mc_type:
; Prosessortyp ermitteln (wichtig für Stack-Aufbau beim TRAP #1)
move.l $5a0,d0 ;Zeiger auf Cookies
beq.s _68000 ;keiner da => altes TOS => MC 68000
movea.l d0,a0
search_lp:
move.l (a0)+,d1
beq.s 68000 ;keine Kennung => altes TOS (s.o.)
cmpi.l #'CPU',d1 ;nach CPU-Cookie suchen
beq.s found
addq.l #4,a0 ;nächsten Eintrag
bra.s search_lp
found:
tst.l (a0) ;Welcher Prozessor?
beq.s _68000 ;0=MC 68000, 10=MC 68010, ...
move.w #2,stk_offs(a5) ;ggf. Stack-Offset erhöhen
_68000:
rts
;
get_wp:
tst.w $43e ;Zugriff erlaubt ?
bne.s get_wp ;nein, warten
moveq.l #4,d0 ;Floppy A:
lea.l WP(a5),a4 ;WP-Statustabelle
lea.l $ffff8800,a3 ;(a3) -> Soundchip; 2(a3) -> PSG-Data (W)
move.w nflops(a5),d3
flop_lp;
move.w #$80,$ffff8606 ;FDC-Statusregister
move.w sr,-(sp)
ori.w #$700,sr ;Interrupts sperren
move.b #$e,(a3) ;PSG Port A selektieren
move.b (a3),d1 ;alten Wert merken
move.b d1,d4
andi.b #$f8,d1 ;die unteren 3 Bits
or.b d0,d1 ;d0=4 -> A: ; d0=2 -> B: (Seite egal)
move.b d1,2(a3) ;in Port A schreiben
move.w $ffff8604,d2 ;FDC-Status nach d2
move.b (a3),d1
andi.b #$f8,d1
btst #7,d2 ;Floppy MOTOR ON ?
bne.s motor
ori.b #6,d1 ;beide Floppies deselektieren
bra.s set
motor:
or.b d4,d1 ;Bei MOTOR ON alten Status setzen
set:
move.b d1,2(a3)
move.w (sp)+,sr
btst #6,d2 ;Ist Floppy A: W.P. ?
sne (a4)+ ;Flag setzen
subq.w #2,d0 ;ggf. noch Floppy B:
dbra d3,flop_lp
rts
;
.DC.w $4d47
vblrout:
;um Registerrettung braucht man sich bei VBL-Routinen nicht zu
;kümmern, das macht schon das TOS !
tst.w $43e ;Floppy-Zugriff erlaubt ?
bne exit ;nein
lea.l varfeld(pc),a5 ;Zeiger auf Variablenfeld
move.w WP(a5),d5 ;alten WP-Status der Floppies merken
bsr.s get_wp ;neuen Status ermitteln
cmp.w WP(a5),d5 ;hat sich was verändert ?
beq.s weiter ;nein!
move.w nflops(a5),d0 ;Anz. der angeschl. Floppies
bne.s testlp
lsr.w #8,d5 ;wenn nur Floppy A:, dann alten Wert verschieben
testlp:
cmp.b WP(a5,d0.w),d5 ;ist Floppy nicht mehr W.P. ? (alt=$FF,neu=$0)
bpl.s no_change ;dann wäre das N-Flag gesetzt!
st akt(a5,d0.w) ;Flag zur Aktualisierung
no_change:
lsr.w #8,d5 ;nächste Floppy
dbra d0,testlp
;
weiter:
tst.w nprgs(a5) ;im Moment Desktop?
bne.s exit ;nein: keine Aktualisierung
move.w defdrv(a5),d0 ;Default-Drive holen
cmpi.w #2,d0 ;Ist's auch keine Hard-/RAM-Disk ?
bcc.s exit ;doch -> raus
tst.b akt(a5,d0.w) ;Muß Inhaltsverzeichnis aktualisiert werden ?
beq.s exit ;nein
movea.l keyparam(a5),a0 ;Zeiger auf Tastaturpuffer-Parameterblock
move.w 8(a0),d1 ;nächste Leseposition
addq.w #4,d1 ;Beim Rauchpilz-TOS durch "addq #2,d1" ersetzen
cmp.w 4(a0),d1 ;Länge überschritten ?
bcs.s erhoeh ;nein
moveq.l #0,d1 ;sonst auf Pufferstart setzen
erhoeh:
cmp.w 6(a0),d1 ;Schreibpos. = Lesepos. (=Puffer voll) ?
beq.s exit ;ja
movea.l 0(a0),a2 ;Zeiger auf Puffer
move.l #$1001b,0(a2,d1.w) ;>ESC< (Scan- & ASCII-Code) in Puffer sehr.
;Beim Rauchpilz-TOS durch "move #$11b,0(a2,d1.w)" ersetzen!
move.w d1,8(a0) ;Zeiger korrigieren
sf akt(a5,d0.w) ;Aktualisierungsflag löschen
exit:
rts
;
.DC.l $58425241
.DC.l $512d442e
old_trp:
.DC.l 0
newtrap:
movem.l d1/d2/a0-a2,regs ;Regs retten,
; weil sich manche Programme darauf verlassen, daß sie unverändert bleiben
lea.l varfeld(pc),a1 ;Zeiger auf Variablenfeld
move.w stk_offs(a1),d0 ;Stack-Offset
lea.l 6(sp, d0.w),a0 ;Parameterzeiger = Supervisor-Stack
btst #5,(sp) ;Auf welchem Stack liegen die Param's?
bne.s super_stack
move.l usp,a0 ;wenn Supervisor-Bit gelöscht ist, sind sie auf Userstack
super_stack:
cmpi.w #14,(a0) ;Dsetdrv() ?
beq.s dsetdrv
cmpi.w #75,(a0) ;Pexec() ?
beq.s pexec
normal:
movem.l regs,d1/d2/a0-a2 ;Reg. wieder herstellen (s.o.)
move.l old_trp,-(sp)
rts ;normalen Trap aufrufen
;
dsetdrv:
move.w 2(a0),defdrv(a1) ;neues Defaultdrive merken
tst.w nflops(a1) ;bei 2 phys. Laufwerken O.K.
bne.s normal
cmpi.w #1,2(a0) ;bei 1 Floppy und neuem Pef.Drv. B: ...
bne.s normal
clr.w defdrv(a1) ;... dieses wie phys. Floppy A: behandeln
bra.s normal
;
pexec:
tst.w 2(a0) ;Exec-Modus = 'laden und starten' ?
beq.s prgstart ;ja!
cmpi.w #4,2 (a0) ;Exec-Modus = 'starten' ?
beq.s prgstart ;ja!
cmpi.w #6,2(a0) ;Neuer Exec-Startmodus bei TOS 1.4 ?
bne.s normal ;nein!
prgstart:
movea.l 4(a0),a2 ;Zeiger auf Programmnamen holen
search0:
cmpi.b #46,(a2) ;Punkt?
beq.s pktgef
tst.b (a2)+ ;String zu Ende
bne.s search0
bra.s aufruf
pktgef:
addq.l #1,a2
move.b (a2)+,d0 ;1. Zeichen
andi.b #$df,d0 ;Großbuchstabe
cmpi.b #65,d0 ;'A' ?
bne.s aufruf ;nein!
move.b (a2)+,d0 ;2. Zeichen (Wordzugriff nicht immer möglich!)
andi.b #$df,d0 ;Großbuchstabe
cmpi.b #$43,d0 ;'C' ?
bne.s aufruf ;nein
move.b (a2)+,d0 ;3. Zeichen
andi.b #$df,d0 ;Großbuchstabe
cmpi.b #$43,d0 ;'C' ?
beq.s normal ;bei 'ACC (=Accessory) normaler Prg.-Start
aufruf:
addq.w #1,nprgs(a1) ;Anzahl der gestarteten Programme +1
movea.l save_area(a1),a2 ;save_area
move.w (sp)+,-(a2) ;SR, ...
move.l (sp)+,-(a2) ;...Rücksprungadresse
tst.w stk_offs(a1) ;noch ein Parameter ?
beq.s mc_alt1 ;nein, alter Prozessor
move.w (sp)+,d0 ;sonst jenen merken
move.w d0,-(a2) ;und retten
mc_alt1:
movem.l d3-d7/a3-a7,-(a2) ;und Register retten
move.l a2,save_area(a1) ;neue Save_area
movea.l a0,sp ;Stack zeigt auf Parameterliste
tst.w stk_offs(a1) ;ggf. noch weiteren Parameter ablegen
beq.s mc_alt2
move.w d0,-(sp) ;auf Stack
mc_alt2:
pea ret ;neue Rücksprungadr.
move.w sr,-(sp) ;und akt. SR
move.l old_trp(pc),-(sp)
rts ;alte TRAP-Routine aufrufen
ret:
lea.l varfeld(pc),a1
clr.w akt(a1) ;bei Prg-Ende nicht 'ESC' simulieren *** !!! ***
movea.l save_area(a1),a2 ; save area
movem.l (a2)+,d3-d7/a3-a7
tst.w stk_offs(a1) ;ggf. auch hier zus. Parameter
beq.s mc_alt3
move.w (a2)+,-(sp) ;wieder zurück
mc_alt3:
move.l (a2)+,-(sp) ;Register, Rücksprungadr. und Statusreg.
move.w (a2)+,-(sp)
move.l a2,save_area(a1) ;alte save.area
subq.w #1,nprgs(a1) ;Anzahl der aufgerufenen Programme -1
rte ;fertig
;
.DATA
texte:
.DC.l t1,t2,t3,t4
t1:
.DC.b 27, "E", 10,10,10,10,13,9
.DC.b " Q - DISK V 1.3 (Dezember '91) ",10,13,9
.DC.b " -------------------------------",10,10,13,9
.DC.b " von Martin Glodde.",10,10,10,13
.DC.b "Das Programm erkennt Diskettenwechsel, wenn ...",10,13
.DC.b "- ... die neue Disk nicht schreibgeschützt ist.",10,13
.DC.b "- ... man sich im Desktop befindet.”,10,10,10,13,0
t2:
.DC.b "ERROR: Kein Platz mehr in der VBL-Queue.",10,13
.DC.b "Q-DISK ist nicht installiert.",10,10,10,13,0
t3: .DC.b "Bitte Taste drücken!",0,0
t4: .DC.b "Programm ist schon installiert!",10,10,10,13,0
;
.BSS
varfeld:
.DS.b 22
regs: .DS.l 5