back : next : content =
 
 

Anhang: Beispiele und aktuelle Ergänzungen

 
 

 

 


 
 

* SQ: Atari-QL, SMSQ, SMSQ/E

Der Leser wird gebeten, eigene Kontrollen vorzunehmen, rsp. nach Konsultation der Original-Dokumente Mängel ggf. beim Verursacher zu reklamieren.

Die sporadisch in den Text aufgenommenen Hinweise auf die SQ-Eigenheiten sind nur ein paar erste Angelpunkte, die bei Bedarf überprüft werden sollten. Vor allem, da so wenige Details bekanntgemacht werden, war das Wenige allein schon der Sicherheit wegen äußerst kritisch einzuordnen.
Dem ließe sich leicht abhelfen: Durch Verzicht auf alberne Geheimhaltungspraktiken...

Wer diesbezüglich Erhellendes beizutragen vermag und geneigt ist, das allein im Interesse der QDOS-"Gemeinde" öffentlich zu machen, ohne jede Aussicht auf direkten finanziellen Gewinn, wird händeringend eben darum gebeten.
 



 

 * program(ier)beispiele

 


* Befristet intallierte entfernbare System-Erweiterungen

Ein Beispiel soll beschreiben, wie Systemerweiterungen in Gestalt von Device-Handlern und Interrupt-Aufrufen an einen Job gebunden werden können, wodurch sie dann ohne weitere Vorkehrungen selbstätig mit dessen Aufgabe in geordneter Form aus dem System verschwinden.

Es wird dazu ein kleiner Timer eingerichtet, der nur als kontinuierlich weitergezählter Langwortinhalt einer memorystelle existiert. Deren besondere Lage erlaubt äußerst schnelle Ausführung und entsprechend geringe Zeitbelastung des System durch diesen Aufruf.

16 Bytes werden für Zähler und Systemliste reserviert:
 
timem dc.l 0,0 hierher zeigt später a3, CH.LXINT
linktime dc.l 0,0 Positionen CH.LPOLL, CH.APOLL
 
Die Interrupt-Routine kann an beliebigem Ort stehen, wobei die Lage

timem = linktime - 8
die besonders einfache Adressierung möglich macht:
 
timer addq.l #1,(a3)
rts
 
Eine Freigabe-Routine ist bereitzustellen:
 
timeunl movem.l a0/a1,-(sp)
lea linktime(pc),a0
moveq #29,d0 mt.rpoll 
trap #1
movem.l  (sp)+,a0/a1
rts
 
Aktiviert wird die Interruptroutine derart:
 
timelnk  lea linktime(pc),a0
lea timer(pc),a1
move.l a1,4(a0)
moveq #28,d0 mt.lpoll
trap #1
rts
 
Nun wird ein Teilprogram eingerichtet, das die sichere Entfernung der betreffenden Handler- oder Interrupt-Routinen aus den Systemlisten vorbereitet. Dazu nutzt man die Vorgänge bei der automatischen Freigabe von Heap-Bereichen, die einem Job zugewiesen wurden. Die geschieht durch das QDOS immer dann, wenn ein Job aufgegeben wird. Es ist daher "nur" dafür zu sorgen, daß das entprechende Teilprogram zuvor die zuätzlich erforderlichen Freigabeproceduren durchläuft. QDOS selbst entnimmt bei jeglicher Form eines Freigabe-Aufrufs die zugehörige Adresse dem Linkblock des betreffenden Handlers; bei Heap-Zuweisungen ebenso.

Ein Heap-Bereich hat in den ersten 16 Bytes einen Header, wie z.B. auch die QDOS-Kanaltabellen. Dort steht auf Offset 4 die Linkadresse CH.LIO des Handlers. Mit Offset 12 dazu ist auf Position CH.ACLOS die Adresse der Freigabe-Routine verzeichnet. Hier besorgt sie die Rückgabe des reservierten Bereichs an den "common heap". Dieser Tabellenkopf ist nun anzupassen:

Man merkt sich die auf Position 4 (CH.DRIVR) vorgefundene Handleradresse. Dort trägt man nun eine Adresse ein, die um 12 niedriger ist als eine neue Tabellenposition (entspr. CH.LIO). An dieser Stelle wird dann die Adresse eines kurzen Umleitungsprograms eingetragen, das ROM-tauglich seinerseits Platz im gerade reservierten Heap finden kann. Dieses kleine Hilfsprogram sorgt für den Sprung z.B. in die oben angegebene Routine zur Entfernung des Timers aus der Interrupt-Liste und danach in die ursprügliche "close"- rsp. Freigabe-Routine, wie sie aus der in CH.DRIVR angegebenen Handleradresse und daraus über CH.LIO und CH.ACLOS ermittelt wurde. Das sieht etwa so aus:

Standard Heap-Header:

[ heap-len ][ handler ][ ownerjob ][ system ][ reservierung ]
Die Basisadresse des nutzbaren Bereichs liefert mt.alchp in a0.
Es wird nun geändert:
[ xx ][ adr1 ][ xx ][ xx ][ jsr timeunl jmp (12(hdl)) ][ adr2 ]
wo (adr1) die adr2-12 enthält, die auf den jsr-Befehl zeigt.
 
linkdev moveq #-1,d2 "aktueller" job ist besitzer
moveq #16,d1 für 2 abs.l sprungbefehle + adresse
moveq #24,d0 mt.alchp
trap #1
move.l d0,d3 ggf. bei memoryüberlauf 
bne.s jobremv ? den job ganz aufgeben
move.l a0,a1 memorybasis merken 
move.w jsrcode(pc),(a0)+
lea timeunl(pc),a2 ablage: jsr timeunlk 
move.l  a2,(a0)+
move.w jmpcode(pc),(a0)+
move.l -12(a1),a2 ablage: jmp memclose
move.l  12(a2),(a0)+
move.l a2,d3 prüfung (notwendig bei smsq!)
bne.s nsmsq ? adresse vorhanden
move.w $0c2,a2 sonst mm_rechp
move.l a2,-4(a0) verwenden
nsmsq move.l  a1,(a0) neuer "close"-pointer zeigt 
move.l a1,-12(a1) nun auf mem+12 = a1+12, wo
rts der hilfsaufruf verzeichnet ist
jobleave moveq #0,d3 keinen fehlercode weitergeben
jobremv moveq #-1,d1 selbst
moveq #5,d0 mt.frjob
trap #1
rts nur, wenn was schief ging
....
jmpcode jmp $11223344 ... zum beispiel, wenn der code
jsrcode jsr $11223344 für jmp... und jsr... unbekannt ist
 
Sind alle diese Vorbereitungen abgeschlossen, erfolgt der Aufruf: .
 
.... program
jsr linkdev(pc)
jsr  timelnk(pc)
.... program
jmp  jobleave(pc)
 
Der Job wird nun unter allen Umständen stets das System geordnet zurücklassen. Auch die Auflösung durch Befehle wie QR oder RJOB wirkt einwandfrei.
 

* Basic-Parameter

Ergänzend ein Verfahren, mit dem der Name einer Variablen geholt werden kann. Vorausgesetzt wird die Registerbelegung wie für die Vectoren zum Holen der sonstigen Basic-Parameter. Zurückgegeben werden die Werte entsprechend dem Aufruf ca.gtstr. Einsprung "nam_vec" erlaubt schrittweise das Holen mehrerer Namen. Auf dieselbe Weise können die Aufrufadressen residenter Funktionen und Proceduren ermittelt werden. Einige dieser Routinen verlangen D7 := 0 (z.B. PRINT und BEEP).

Die reichlichen Sicherheitsabfragen müssen nicht immer erforderlich sein. Sie sind als Schutz bei smasheden Daten gedacht, und evtl. auch nützlich für den Fall mangelhafter Dokumentation des Systems. Namen werden in Zusammenhang mit einer möglichst sicheren Kontrolle gegen Fehler in der Länge auf 127 Bytes begrenzt.

* REIN: a3 a5 a6 Basic-Register der Namen und der Jobbasis
* RAUS: d0 Fehlercode d3 Anzahl (1/accu) a1 String-Basisadresse
* VERDIRBT: d1 d2 d3 d4 a0 a2 und paßt a3 bei Erfolg an
nam_ftch moveq #0,d3 erstmaligen aufruf vorbereiten
nam_vec moveq #6,d0 err.nf vorbereiten
moveq #15,d2 typenmaske
and.b 0(a3,a6.l),d2 aus dem use-byte
beq.s nam_fnd ? übergabe kann gelten
subq.b #1,d2
beq.s get_nich ? ausdruck
subq.b #8,d2
bgt.s get_nich ? verwendung rätselhaft 
nam_fnd move.l a3,a0 copie zum suchen
move.w 2(a0,a6.l),d1 name-index
not.l d1 gegen -1 absichern
beq.s get_nich ? doch ein ausdruck
not.l d1 wert wiederherstellen
ext.l d1 vereinfacht die prüfung
lsl.l  #3,d1 des ermittelten ptr
** Rom-Versionen vor JS ohne Addition aus bv.ntbas von 24(a6) **
nam_tab add.l 24(a6),d1 position in der name-table
btst #0,d1 bei smasheden daten
bne.s get_nich  ? adressenfehler vermeiden
move.l d1,a0
move.w 2(a0,a6.l),d1 ptr zum namen
bmi.s  get_nich ? weil es den namen nicht gibt
move.w d1,a0 ptr sichern
move.l  32(a6),d1 basis der name-list
add.l  a0,d1  ptr auf den namen
moveq #0,d4 register freimachen
move.b 0(a6,d1.l),d4 count.b holen
ble.s get_nich  ? zu lang oder leer
move.l d4,d1 übertragen
addq.l  #3,d1 dazu count.w der ablage
and.w #-2,d1 geradzahlig
move.w d3,-(sp) accu kellern
move.w $11a,a2 mit bv.chrix
jsr (a2) basic-memory reservieren
move.w (sp)+,d3 accu zurückholen
moveq #3,d1 erneute längenermittlung
add.l  d4,d1 geschieht schneller als die
and.w #-2,d1 stackablage
move.l $58(a6),a1 zum alten wert
sub.l  d1,a1 neue länge im übergaberegister
move.l a1,$58(a6) auf bv.rip fixieren 
move.l a1,a2 arbeiscopie
move.w d4,0(a2,a6.l)  länge eintragen 
subq.w #1,d4 zähler
add.l 32(a6),a0 bv.chrix kann ptr verändert haben
nam_put move.b 1(a0,a6.l),2(a2,a6.l)
addq.l #1,a2 byteweise
addq.l  #1,a0
dbra d4,nam_put alle zeichen übertragen
addq.l #8,a3 parm-ptr justieren
addq.w #1,d3 anzahl eins weiter
moveq #-1,d0 und ohne fehler
get_nich not.l  d0 ggf. err.om wiederherstellen
rts
 
Damit kann ein universeller Aufruf, der statt eines String automatisch den Namen holt, wenn er als Variable nicht definiert ist, so aussehen:
 
stg_parm moveq #0,d3 zähler vorbereiten
stg_nxtn move.l 4(a3,a6.l),d2 werte-ptr
not.l  d2
beq.s stg_name ? vari ohne wert
tst.w d3 wenn schon namen da sind
bne.s inp_rdy ? besser nicht zum vector
stg_name bsr.s nam_vec > den namen als string holen
beq.s stg_nxtn ? weiter (ver)suchen
tst.w d3 anzahl
beq.s inp_tst ? weiter mit 'mi' bei fehler 
inp_rdy moveq #0,d0 fehlercode war schlußsignal
bra.s inp_oke - zur auswertung
inp_vec move.w $0116,a2 ca.gtstr als
jsr  (a2) > regulärer aufruf
inp_tst tst.l  d0 fehlercode prüfen
bne.s inp_err ? es gab einen fehler
inp_oke .... .... auswertung und
inp_err .... .... fehlerbehandlung
 
 
 



 
 

* System-Erkennung

Es gibt verschiedene Emulatoren des QL in den verschiedensten Systemen. Sinn solcher programe ist es u.a. auch, den QL so nachzubilden, daß die Tatsache der Emulation aus dem "Innern" nicht erkennbar wird. Andrerseits gibt es dort im allgemeinen wesentliche Lücken, aufgrund derer manches QL-program entweder nur in besonderer Variante oder garnicht  einsetzbar ist. Solche programe scheitern meist an einer dem Anschein nach wohl vorhandenen, nicht aber originalgetreu nachgebildeten Funktion des Rechners.

Zur Prüfung gibt es verschiedene Mittel, wie auch verschiedene Kriterien. Unabdingbare und entscheidende Voraussetzung ist allerdings, daß für die Emulation die QDOS-Konventionen und Entsprechendes der diversen Systemerweiterungen (TK2, etc) strikt beachtet wurden.

Wer ein Nicht-Standard-System nachbildet, sollte insbes. die gerade zur Unterscheidung der Varianten eingerichteten Variablen nutzen, ebenso, und auch bei allen anderen Emulationen, die weiteren eindeutig festgelegten Hilfmittel - oder die Nachbildung hinreichend exakt machen (wie z.B. QLAY).
Dies unbeachtet zu lassen, stiehlt den durch die zu erwartende Fehlfunktion gequälten Anwendern unnötig an solch untaugliche Produkte vergeudete Zeit. "Alpha"- & "Beta"-Ausreden sind da nicht sonderlich glaubhaft.
 

Filesystem

Dieser Test ist einfach, denn jedes System, in dem die Trap FS.XINF (3/79) fehlerfrei ausgeführt werden kann, gilt als "level 2" Filesystem, wo die zusätzlicche Funktionalität grundsätzlich gegeben ist, und Ergänzungen zur Ur-Version mittels der zurückgegebenen Daten überprüft werden können.
 

I.P.C.

1. Direkt:
Vorhandensein des wirklichen I.P.C. kann mit dem dazu vorgesehenen Aufruf TEST.CMD der Trap MT.IPCOM (1/17) festgestellt werden: Nur, wenn der I.P.C. entweder physisch vorhanden und in Ordnung ist, oder die Emulation ihn ohne jede Ausnahme vollständig und korrekt nachbildet, wird das Parameterbyte der Definition gemäß unverändert zurückgegeben.
Einmalige Wiederholung der Abfrage mit einem anderen Parameterbyte genügt, um zufällige Ergebnisse auszuschließen.

2. über Systemvariable
Liefert das Uhrenregister über mehrere Sekunden hinweg stets denselben Wert, ist sicher irgendeine Emulation in Betrieb, und der IPC ist nicht vorhanden oder äußerst mangelhaft nachgebildet.
 

Processortyp

Von Experimenten mit speziellen Processorbefehlen, die nur auf dem einen oder dem anderen Gerät ausführbar sind und sonst in eine Ausnahem-Trap führen, wird dringend abgeraten. Die Folgen sind oft unangenehmer, als beim Betrieb des betr. ungeeigneten programs (programe z.B., wo direkt codiert an den Cache-Zuständen herumgefummelt wird).

Es gibt sichere Alternativen! Als da sind:

1. Der Cache-Test mit SMS.CACH (1/47) gibt zum einen Aufschluß über den Status der Processorcache, zum andern aber auch, ob überhaupt ein geeigneter Processor installiert ist. Bei 6800x oder 6801x kommt ERR.BP zurück, da es dort dergleichen nicht gibt. Auch die üblichen Emulatoren werden, sofern sie nicht eine SQ-Vaiante abbilden, im allgemeinen die ERR.BP-Rückgabe auslösen.
Die Trap arbeitet beim Lesen ohne jede Ausweirkung auf das Sytem, kann also sicher zum Test benutzt werden.

2. Systemvariable
Die Posten SYS.PTYP ($A1) und SYS.MTYP ($A7) geben recht detaillierte Aukunft  über Processor-, Bildschirm-  und Geräte-Typ, sofern sie denn vom System bedient werden - was leider nicht in allen anwendbaren Fällen zutrifft.
Mehr dazu an entsprechender Stelle im Tabellenteil.

Wer ein Nicht-Standard-System nachbildet, sollte diese Variablen nutzen, und sie ggf. durch Anmeldung an geeigneter Stelle "offiziell" machen. Er vergibt sich damit nichts!
Sie unbeachtet zu lassen, beraubt den durch die zu erwartende(!) Fehlfunktion gequälten Anwender nur der an solch untaugliche Erzeugnisse vergeudeten Zeit. Und es ist sicherlich auch schade um den Rest der ansonsten vielleicht ganz vortrefflichen Arbeit, deren Wert durch Fehlen eines kleinen aber bedeutsamen Details zunichte gemacht wird.
 

Kanaltyp

Ungeeignete Vorgehensweise führt gerade hier in vielen existierenden programen leicht zu Fehlern.

So ist etwa von vornherein offensichtlich, daß insbes. die Größe einer cdt kaum zur Unterscheidung brauchbar ist. Gerade dieser Wert hat aus der Sicht einer Anwendung in weitestem Sinne nicht das geringste mit einem bestimmten Kanaltypen zu tun.

Wenn dennoch aus der Größe der CON-cdt auf Vorhandensein des PIF oder einer anderen CON-Erweiterung geschlossen wird, mag das angehen, und es ist wegen der u.U. gänzlich nutzlosen Prüfroutinen des PIF mitunter das einzige Mittel.
Handlervergleich mit Kanal 0 ist aber für CON-Kanäle, falls nicht gerade ein Wahnsinniger ausgerechnet diesen wichtigen QDOS-Kanal geschlossen oder umdefiniert hat, die schellere und zuverlässigere Prüfung.

Und ist zwar Lesen der Bildparameter mit z.B. SD.CHENQ (3/11) möglich, oder liefert es ERR.NC (-1), der Handler aber nicht derselbe wie der des Kanals 0, so hat man es mit einem SCR-Kanal zu tun.

Als weiteres Mittel, das insbes. in größeren oder über einen längeren Zeitraum zu betreibenden programen probat ist, bietet sich vorübergehendes Öffnen eines Prüfkanals der gewünschten Art an, memoryn dessen Handleradresse in den eigenen Variablen, und fortan Analyse der fraglichen Kanäle anhand einer so gewonnenen Sammlung.

Soll aber z.B. die exakte Länge 160 als Kriterium für einen Directory-Device-Kanal herhalten, so ist das nicht nur verfehlt, sondern geht auch weit an der längst bekannten Dokumentation vorbei (im Original heißt es "mindestens 160  Bytes..."). Geeignete programierung vorausgesetzt, ist die dadurch gewonnene Auskunft zudem völlig überflüssig. Wird sie aber aus welchem Grunde auch immer dennoch benötigt, liefern die Handler-Listen des Systems die gesuchten Angaben, ohne daß irgendwelche Daten "gewußt" werden müßten, und das in völlig systemunabhängiger Form.

Gerade die Unterscheidung zwischen Kanälen des einfachen und des Directory-Typs ist aufgrund der vielen im System verfügbaren Angaben außerordentlich einfach, sicher und von der aktuellen Konstellation unabhängig durchführbar.

Das geht hin bis zur vollständigen Bestimmung ganzer Kanalnamen, die gerade für Directory-Devices mit Hilfe allein der verschiedenen Systemvariablen möglich ist.

Mehr auch hierzu an entsprechender Stelle im Tabellenteil.
 

RAM/ROM-Test

Es ist gelegentlich erforderlich, zu wissen, ob das Betriebssystem im RAM liegt, oder im ROM rsp. einem hardwaremäßig schreibgeschützten Adressenbereich. Das läßt sich unabhängig vom betr. Gerät und dem eingesetzten Processor mit folgender Codepassage in Erfahrung bringen:
 
trap #0 (supv)
move.l sp,d2 leerer stack
moveq #0,d0 prüf-flag "System im ROM"
move.w #8,a2 bus-error vector
move.l (a2),-(sp) lesen geht immer
pea (a2) vector
move.l sp,d1 fehler-stack merken
pea berr(pc) vector und zur prüfung
move.l (sp),(a2) springt ggf. in exception <berr>
cmp.l (sp)+,(a2)+
seq d0 ? 'ne' bei code im ROM
move.l d1,sp bus-error-trap restaurieren
move.l (sp)+,a2 ROM oder nicht ist 
move.l (sp)+,(a2) hier nun unbedeutend
berr move.l d2,sp ursprünglicher stackptr
and.w #$dfff,sr (user)
 
 
 
 


 
top : back : next : content 

= (count)