So mancher ernsthafte Entwickler hat sich diese Frage schon gestellt: Gibt es ein Verfahren, bei dem das Programm selbst die wildesten Abstürze und sogar einen Reset überlebt und weiterhin seinen Dienst verrichtet? Wie kann ich ein Programm resetfest im Speicher verankern?
Wer kennt das Problem nicht? Hat man sein System mit allen Extras wie Mausbeschleuniger, RAM-Disk, Bildschirmschoner usw. ausgerüstet, kann es allzu leicht Vorkommen, daß man ein unsauber geschriebenes Programm startet, was anscheinend nichts Besseres zu tun hat, als Ihren ST derartig zu verwirren, daß er (vielleicht nach Ausgabe einiger letzter Bömbchen) sich hoffnungslos in seinen Innereien verstrickt. Falls der Rechner das nicht schon für Sie erledigt hat, bleibt nur noch der Griff hinter Ihren ST zu dem kleinen grauen Taster, der ihn aus dem Dornröschenschlaf erwecken sollte. Jetzt die Startdiskette wieder einlegen, die RAM-Disk, die gerade Ihre wichtigen Daten in den Datenhimmel geschickt hat, neu konfigurieren, Bildschirmschoner und Mausbeschleuniger neu laden und einstellen, Zeit und Datum neu setzen ... Als Assembler-Programmierer machen Sie sich dann Gedanken, wie man diesem Übel abhelfen kann und kommen zu dem Schluß, daß man einen großen Teil des Betriebssystems neu schreiben müßte, wenn man Programme über den von ATARI dokumentierten Reset-Vektor resetfest machen wollte. Also doch alles neu booten?
Nein! Denn es gibt ein Verfahren, das es ermöglicht, eine ganze Reihe von Programmen nebeneinander resetfest im Speicher zu halten. Doch um das zu verstehen, muß man sich erst einmal klarmachen, was bei einem Reset so alles passiert.
Es gibt Hard- und Softwareresets. Der Hardwarereset findet über den Reset-Taster statt, der bewirkt, daß die Leitungen RESET und HALT des Prozessors auf LOW gehen. Alle anderen Resets sind durch die Software verursacht worden, indem der Programm-Counter des Prozessors einfach in die Routine läuft, die nach jedem Hardwarereset durchlaufen wird. Außerdem gibt es noch den sogenannten Tastaturreset, der entweder im Betriebssystem implementiert ist (Rainbow-TOS: Alternate/Control/Delete) oder durch ein Programm, das vorher installiert werden muß, beim Drücken von bestimmten Tastenkombinationen verursacht wird. Beim Reset per Reset-Taster (und normalerweise auch beim Tastatur-Reset) wird der Prozessor in den Supervisormodus umgeschaltet und der Stackpointer mit dem Inhalt der Speicherstelle $0 sowie der Programm-Counter mit dem der Speicherstelle $4 geladen, die auf die Routine verweist, die normalerweise bei jedem Reset angesprungen wird. Leider sind die bewußten Speicherstellen $0 und $4 ROM-Speicher, und zwar ein Shadow der ersten acht Bytes des eigentlichen ROMs (normalerweise ab $fc0000 zu finden). Man kann sie also nicht per Programm verändern. Deshalb hat ATARI im Reset-Handler eine weitere Funktion eingebaut.
Nach Initialisierung des SSP (Supervisor StackPointer) und des PC (ProgramCounter) wird der IPL (Interrupt Priority Level) auf $7 (keine Interrupt-Behandlung) gesetzt und der Befehl RESET ausgeführt, der die RESET-Leitung des Prozessors für 124 Taktzyklen (15 usec) auf LOW setzt, wodurch der Peripherie ein Reset vermittelt wird. Danach wird ein eventuelles Cartridge-Programm (ROM-Modul) ausgeführt und dann der RAM-Speicher konfiguriert (wenn er es nicht mehr sein sollte). Nun wird die Speicherstelle $426 (res-valid) geprüft: Enthält sie die „Magic Number“ $31415926 (die ersten acht Stellen von PI!), wird der Programmzähler mit dem Inhalt der Adresse $42a (resvector) geladen, mit einer Rücksprungadresse im Adreßregister a6. Leider kann mittels dieses Reset-Vektors kein Programm speicherresident gemacht werden, da es absolut unsinnig wäre, zu diesem Zeitpunkt z.B. eine resetfeste RAM-Disk zu installieren, denn wenn man sämtliche Arbeiten des Resethandlers nicht selbst übernehmen will (Boot-Sektor, Harddisk, AUTO-Ordner usw.), muß man nach Ablauf des eigenen Programms wieder in ihn zurückkehren. Nun werden aber die Vektoren neu initialisiert, d.h. eine RAM-Disk würde gar nicht erst aktiv werden. Die ganze Arbeit des Reset-Vektors zu übernehmen, wäre ebenso unsinnig, da man dann entweder illegal programmieren (direkte Sprünge in das ROM des Betriebssystems, was zu Inkompatibilitäten führen würde) oder alles selbst programmieren müßte, was das Rad neu zu erfinden hieße. Außerdem kann es bei Betriebssystemaufrufen nur allzu leicht „bombig“ werden, so daß davon auch abzuraten ist. Routinen über den Reset-Vektor sind also nur sinnvoll, wenn sie entweder einen Reset unterbinden sollen oder den Rechner nur kurzzeitig während des Resets beeinflussen. Ansonsten ist er für unsere Zwecke, ein ganzes Programm resident zu machen, ungeeignet. Also sind resetfeste Programme unmöglich? Nein, denn es gibt noch eine weitere Möglichkeit, in den Programmablauf des Betriebssystems nach bzw. während des Resets einzugreifen.
Gesucht wird nämlich eine Möglichkeit, nach der Initialisierung des ganzen Systems samt Hardware in den Programmablauf nach dem Reset einzugreifen. Gefunden habe ich diese Möglichkeit im „ATARI ST Profibuch“ (und im reassemblierten Auszug aus meinem Betriebssystem), allerdings ist es nicht leicht gewesen, sie zu realisieren, da dort einige wichtige Punkte nicht berücksichtigt oder weiter ausgeführt werden. Nach dem Booten von der Festplatte und der Ausführung eines evtl. Boot-Sektor-Programms durchsucht das Betriebssystem den Speicher nach einer Speicher-Doppelseite (512 Byte) mit folgenden Eigenschaften:
Hat das Betriebssystem eine solche Doppelseite gefunden, verzweigt es in diese Seite (für Insider: mittels jsr, Einsprungadresse ist Doppelseitenadresse+8, Supervisormodus ist an, Register sollten geschützt werden). Die Suche beginnt bei phystop ($42e, dazu gleich mehr) und läuft in $200er-Schritten abwärts bis einschließlich $600. Da der Bereich zwischen $800 und $10000 aber vorher gelöscht wird, kommt nur noch $600 in Frage, da die Bereiche darunter Vektoren des Betriebssystems enthalten und der Speicher oberhalb von $10000 von Programmen genutzt wird. Platz ist also da - aber nur für ein Programm. Im Profibuch wird eine allgemeine Lösung beschrieben, die ich ebenfalls eingeschlagen habe. Der eigentliche Programmcode solle oberhalb von phystop (s.u.) abgelegt werden, während bei $600 nur ein sog. „Händler“ stehen soll. Dieser Handler verwaltet eine Liste, die ab $680 zu finden ist: Sie umfaßt (hier) 32 Einträge von Programmen, die über den Handler aufgerufen werden können. Ist der Eintrag $0, passiert gar nichts, sollten aber Adressen in die Liste eingetragen sein, verzweigt der Handler (wenn er nach dem Reset aufgerufen wird) nacheinander in sie. Auf diese Art und Weise können mehrere Programme nebeneinander im Speicher reset-resident gehalten werden. Doch bevor ich konkret das Verfahren erkläre, muß ich noch einiges über phystop und den Trick, die resetfesten Programme oberhalb dieser Adresse abzulegen, sagen.
Vorgreifend ist erst einmal eine kleine Erläuterung der Speicherverwaltung im ST notwendig. Bei der Initialisierung des Speichers (nach Anschalten des Rechners) wird festgestellt, wieviel RAM vorhanden ist. Da das eine recht komplizierte Sache ist, wird das nur einmal getan, worauf verschiedene Register gesetzt werden und memvalid ($420) auf den Wert $752019f3 sowie memval2 ($43a) auf $237698aa gesetzt werden. Bei jedem Reset wird geprüft, ob diese Werte noch in den Speicherstellen zu finden sind. Ist das nicht der Fall, wird der Speicher neu konfiguriert. Ansonsten wird die alte Konfiguration einfach übernommen (das wäre ein sog. „Warmstart“, während bei einer Speicherkonfiguration dasselbe abläuft wie beim Einschalten des Rechners, was Kaltstart genannt wird). Beim Kaltstart wird durch das Ergebnis der Speicherkonfiguration auch die Speicherstelle $42e (phystop) beeinflußt: Das Betriebssystem merkt sich in ihr die Speicherhöchstgrenze und stimmt die gesamte Speicherverwaltung des GEMDOS' darauf ab. Bei einem 1040er steht dort zum Beispiel $ 100000 - das erste Byte nach Ende des physikalischen RAM-Be-reiches. Beim Reset als Warmstart wird diese Adresse nicht neu gesetzt und die Speicherverwaltung auf sie abgestimmt. Das heißt konkret, daß die 32 KB Video-RAM direkt unter dieser Höchstgrenze angesetzt werden und der maximale Arbeitsbereich für Programme sich von $f8000 an abwärts erstreckt. Will man nun ein oder mehrere Programme resetfest machen, muß man dafür sorgen, daß sie beim nächsten Programmaufruf nach dem Reset nicht zerstört (z.B. durch Überschreiben) werden, d.h. sie müssen die Zeit nach dem Reset ebenso überleben. Nun hat man zwei Möglichkeiten: Entweder hängt man sich in die GEMDOS-Speicherverwaltung und blockiert so die Vergabe des dem eigenen Programm zugeordneten Speichers, oder, was die einfachere Lösung ist, man setzt phystop herab, um dem Betriebssystem einen kleineren Speicher „vorzugaukeln“, um anschließend sein Programm oberhalb von phystop abzulegen. Da GEMDOS nicht ständig seine Speicherverwaltung nach phystop ausrichtet, sondern nur nach einem Reset, muß man mit dem Kopieren bis kurz vor diese Ausrichtung warten. Was ist dazu besser geeignet als der Reset-Vektor?
Zunächst wird sichergestellt, daß ein Handler ab $600 zu finden ist (notfalls wird ein eigener installiert). Dann wird eine Routine, die das Verschieben der Hauptroutine besorgt, und die phystop herabsetzt, in den Reset-Vektor eingehängt. Bei einem Reset wird phystop also herabgesetzt und die Routine oberhalb von phystop abgelegt. Voraussetzung dafür ist es, daß die bewußte Hauptroutine vollständig relokatibel ist, d.h. nur PC-relative Adressen und Sprünge verwandt werden. Außerdem ist es wichtig, zu beachten, daß phystop nur in $200er-Schritten verändert werden darf, selbst wenn die Routine kürzer ist. Falls phystop z.B. nur um $80 herabgesetzt wird, passiert gar nichts mehr, denn dann wird bei einem Reset nicht die Speicher-Doppelseite ab $600 getestet, sondern die ab $980, die ab $780 und vielleicht noch die ab $580, was jedesmal zum Mißlingen der Suche führen muß. Denn das Betriebssystem nimmt phystop als Startwert und subtrahiert bei jedem Test stur $200, ohne darauf zu achten, ob phystop auf eine gerade Doppelseitenadresse (z.B. $800,$600) zeigt. Also in der Reset-Vektor-Routine darauf achten, daß phystop nur auf eine Speicher-Doppelseite mit gerader Seitennummer zeigt, sonst passiert gar nichts. Nachdem sie die Adresse der Hauptroutine in der Liste ab $680 eingetragen und die Prüfsumme berichtigt hat, sollte sich die Routine aus dem Reset-Vektor ausklinken und (wenn keine Nachfolgeroutine per XBR A da ist) wieder in die normale Reset-Routine zurückverzweigen. Dabei ist zu beachten, daß a6 die Rücksprungadresse enthält, wobei bei einer TOS-Version a6 um $24 korrigiert werden muß (lt. Atari ein Tippfehler im Betriebssystem). Diese läuft dann normal ab, bis zu dem Punkt, wo die Doppelseiten-Suche beginnt. Das Betriebssystem findet dann nämlich den Handler ab $600 und verzweigt in ihn. Dieser wiederum ruft die installierten, vor jeglichem Überschreiben geschützten Routinen oberhalb von phystop, denen gemäß GEMDOS’ Speicherverwaltung nun ausgelegt ist, auf. Soviel zur allgemeinen Methode. Jetzt wird’s etwas spezieller:
Das kleine Beispielprogramm „Kuckuck“ demonstriert recht anschaulich, wie man ein Programm auf diese Art und Weise komfortabel reset-resident machen kann. Zunächst testet das Programm anhand seiner Kennung, ob es bereits installiert war. Dazu durchsucht es (falls resvalid korrekt ist) die XBRA-Kette im Reset-Vektor nach einem Programm mit der Kennung „KUCK“. Ist es dort nicht fündig geworden, testet es, ob bereits ein „Händler“ ab $600 installiert ist, indem es genauso wie das Betriebssystem vorgeht. Ist das der Fall, wird die Sprungliste des Handlers ab $680 nach einer Routine durchsucht, die zwei Bytes hinter ihrer Einsprungadresse ebenfalls die Kennung „KUCK“ aufweist. Falls eine solche Routine gefunden wurde, bricht das Programm mit der Meldung „Kuckuck! war bereits installiert!“ ab. Wenn überhaupt noch kein Handler installiert war, installiert das Programm seinen eigenen Handler, trägt aber noch keine Adresse ein. Das geschieht erst während des Resets in der im Reset-Vektor eingehängten Routine, zusammen mit der Korrektur der Prüfsumme und dem Verschieben der Hauptroutine. Hat das Programm seine Routine im Reset-Vektor eingehängt sowie den Handler evt. installiert, bleibt es zunächst speicherresident, um auf einen Reset zu warten. Dann passiert genau das, was ich oben bereits beschrieben habe: Die Hauptroutine wird über ein herabgesetztes phystop kopiert und die Adresse ab $680 eingetragen sowie die Prüfsumme korrigiert. Die Hauptroutine macht eigentlich nichts anderes als „Kuckuck! [Taste]“ auf den Bildschirm zu bringen und dann auf eine Taste zu warten. Ich hätte genauso gut eine leere Routine an diese Stelle schreiben können, da sie eigentlich unsinnig ist, aber zu Demonstrationszwecken sollte die Routine nur einen kurzen Text auf den Bildschirm bringen, der anzeigt, daß das Programm jetzt eben resident ist.
Wie man „Kuckuck!“ wieder los wird, außer durch einen Kaltstart bzw. durch das Aus- und wieder Einschalten des Rechners, zeigt das Programm REM_KUCK. Durch dieses Programm wird es möglich, beim gleichzeitigen Einsatz mehrerer resetfester Programme bzw. Routinen EINE herauszunehmen und aus dem Speicher zu entfernen, ohne die anderen Routinen zu beeinträchtigen, und ohne daß „resetfeste Speicherlöcher“ entstehen. Es entfernt den installierten Kuckuck nämlich wieder aus dem Speicher, indem es seine Einsprungadresse sucht und phystop wieder um „Kuckuck!“s Länge ($200) heraufsetzt. Danach wird alles, was unterhalb von Kuckuck und oberhalb von phystop an Routinen im Speicher stand, nach oben verschoben und deren Adressen im Handler ebenfalls korrigiert. Wenn aber die erste (die mit der niedrigsten Einsprungadresse) Routine nicht mit dem momentanen Stand von phystop übereinstimmt, bleibt der von Kuckuck! belegte Speicherplatz oberhalb von phystop bis zum nächsten Kaltstart bzw. Einschalten ungenutzt. REM_KUCK gibt dann eine entsprechende Fehlermeldung aus. Falls „Kuckuck!“ noch nicht im Handler installiert war, sondern nur im Reset-Vektor, wird Kuckuck aus einer eventuellen XBRA-Kette ausgeklinkt bzw. der Reset-Vektor desaktiviert, falls es die einzige Routine war. Der mittels Ptermres belegte Speicherblock kann leider nicht wieder freigegeben werden. Jegliche Versuche enden mit GEMDOS-Fehler #40 (falsche Blockadresse, siehe GEMDOS-Funktion #73: Mfree). Dieser Speicherblock wird erst wieder beim nächsten Reset frei. Wen es reizt, und wer sich mit der GEMDOS-internen Speicherverwaltung auskennt, der kann ja die Programme zum Entfernen resetfester Routinen um eine Routine erweitern, die eben diesen Speicher wieder freigibt.
Wenn man Programme nach diesem Verfahren resident macht, sollte man sich an einheitliche Richtlinien halten, um Fehler zu vermeiden und um zu ermöglichen, daß mehrere Programme nebeneinander resetfest bleiben können. Das heißt konkret:
Wenn sich alle Programmierer, die ihre Programme mittels dieses Verfahrens resetfest machen, an diese Konventionen halten, steht einem reibungslosem Miteinander der verschiedensten resetfesten Programme nichts mehr im Wege. Jedes Programm kann anhand seiner Kennung feststellen, ob es schon installiert ist bzw. wo es steht und sich aus dem Speicher entfernen muß, um sich zu jeder Zeit wieder ausklinken zu können, ohne daß Speicher ungenutzt bleibt (oberhalb von phystop entstünden sonst Speicherlöcher). Alle Programme haben uneingeschränkten Zugriff auf alle Register und alle BIOS-, XBIOS- und GEMDOS-Funktionen des Betriebssystems.
Der Assembler-Source-Text des Hauptprogramms „KUCKUCK!“ und des Programmes „REM_KUCK“ (zum Entfernen des ersten) sind heftigst dokumentiert, so daß sich eigentlich keine Schwierigkeiten ergeben dürften. Achten Sie beim Assemblieren darauf, daß die resetfeste Routine relokatibel bleibt! Ein BASIC-Lader wäre bei derartigen Programmen, die eigentlich nur zur Veranschaulichung eines „neuen“ Verfahrens dienen, unangebracht, da dieses Verfahren nur für den versierten Assembler-Programmierer in Frage kommt. Für diejenigen unter Ihnen, die mit der Sprache Assembler nicht vertraut sind, lohnt sich aber ein Blick auf „Anwendungen“ und die Leserservice-Diskette.
Der Phantasie des Programmierers sind nur wenige Grenzen gesetzt: Zum Beispiel könnte man ein Programm schreiben, daß das Betriebssystem individuell konfiguriert (Druckereinstellung, Modemparameter, Farben, Startauflösung, Bootdevice) oder gar ganze Utilities resetfest halten (RAM-Disk, Drucker-Spooler, Maustreiber, Bildschirmschoner, Virenwächter usw.). Eine „vernünftige“ Anwendung eines resetfesten Programmes habe ich bereits realisiert: ein kleines Programm, das die „vergeßliche“ GEMDOS-Uhr ständig (auch nach einem Reset) auf dem aktuellen Stand hält, indem es nach einem Reset die Zeit und das Datum aus der IKBD-Uhr in die GEMDOS-Uhr kopiert. So hat man ständig die aktuelle Zeit und das aktuelle Datum. Wenn man sich einmal daran gewöhnt hat, möchte man das Programm nicht mehr missen, zumal jetzt endlich die Datei-Erstellungsdaten stimmen, was sehr vorteilhaft ist, wenn man viel schreibt. Man wundert sich direkt, wieviele Uhren auf einmal richtig gehen. Da das Programm mit einer automatischen Abfrage von Zeit und Datum nach dem Einschalten und diversen Extrafunktionen (deren Routinen aber nicht resident bleiben) zu lang für einen normalen Artikel geworden ist, finden Sie es, zusammen mit dem ausführlich dokumentiertem Quelltext in Assembler, auf der Leserservice-Diskette. Das Programm zum schonenden Entfernen der resetfesten Routinen fehlt natürlich auch nicht.
Literatur:
ATARI ST Profibuch, Jankowski/Reschke/Rabich, SYBEX, ISBN 3-88745-563-0
; ###############################################
; ## *** KUCKUCK! *** ##
; ## RESETFESTES DEMONSTRATIONSPROGRAMM ##
; ## by ROLAND NITZ (c) 1992 MAXON Computer ##
; ###############################################
.TEXT
START: bra MAIN
; » WEGEN RESIDENT-BLEIBEN... «
; ** WIRD PER RESETVEKTOR ANGESPRUNGEN...***+
; KOPIERT "RESET-ROUTINE" AN EINGETRAGENE ADRESSE
; (SIEHE OBEN), SETZT PHYSTOP HERAB UND KLINKT
; SICH DANN AUS EINER EVT. XBRA-KETTE WIEDER AUS.
.DC.b "XBRA" ; XBRA-PROTOKOLL
.DC.b "KUCK" ; PROGRAMMKENNUNG
OLDRV: .DC.l 0 ; ; ALTE ADRESSE
RESVEC: movem.l d0-d2/a0-a2/a7,SAVREGS
lea.l MAIN+1052, sp ; STACK INIT.
movea.l $42e,a2 ; PHYSTOP HOLEN
suba.w #((ERT-SRT+$1ff)&$200),a2
; ; VERSCHIEBUNG
lea.l SRT,a0 ; STARTADRESSE
movea.l a2,a1 ; COPY-ADRESSE
move.l a1,$42e ; NEUES PHYSTOP
move.w #(ERT-SRT)/4,d0
.COPY: move.l (a0)+,(a1)+ ; KOPIERE DATEN
dbra d0,.COPY ; NÄCHSTEN...
; » ADRESSE IM HANDLER EINTRÄGEN
lea.l $680,a0 ; SPRUNGLISTE
moveq.l #32-1,d0 ; MAX. 32 POS.
.TSTPOS: tst.l (a0)+ ; IST 0?
beq .OKPOS ; JA: OK!
dbra d0,.TSTPOS ; WEITERSUCHEN
tst.w d0 ; D0 NEGATIV?
bmi .FAILIT ; JA: AUS.
.OKPOS: move.l a2,-4(a0) ; ROUT. EINHÄNGEN
bsr CHCKSM ; PRÜFSUMME KORR.
.FAILIT: lea.l $42a,a1 ; BASISADRESSE
.TST: cmpi.l #RESVEC,(a1) ; BIN DAS ICH?
beq .CPY ; JA: KOPIERE...
movea.l (a1),a2 ; ADR. HOLEN
cmpi.l #"XBRA”,-12(a2) ; XBRA?
bne .REMOVE ; NEIN: DANN CLR!!
lea.l -4(a2),a1 ; FÜR NÄCHSTEN LAUF
tst.l (a1) ; ADRESSE 0?
bne -TST ; NEIN: WEITER
bra .EXIT ; JA: RAUS (??!)
.CPY: move.l OLDRV,(a1) ; SONST SETZEN
tst.l $42a ; RES_VECTOR=0?
bne .EXIT ; NEIN: WEITER
.REMOVE: clr.l $426 ; RESVALID=INVALID
.EXIT: tst.l OLDRV ; NOCH EINE ROUTINE?
beq .CONT ; NEIN: WEITER
movem.l SAVREGS,d0-d2/a0-a2
move.l OLDRV,-(sp) ; ALTE ROUTINE
rts ; UND ZURÜCK
.CONT: cmpi.w #$9bcd,(a6) ; FEHLERHAFTES TOS?
bne .GOON ; NEIN: NORMAL WEITER
lea.l $24(a6),a6 ; SONST $24 ADDIEREN
.GOON: movem.l SAVREGS,d0-d2/a0-a2/a7
; ; REGISTER RETTEN
jmp (a6) ; UND WEITERMACHEN
;*** HANDLER ***
SHANDLER:
.DC.l $12123456 ; MAGIC NUMBER
.DC.l $600 ; ZEIGER
movem.l d0-d7/a0-a6,-(sp)
movea.w #$680,a0 ; STARTADRESSE
move.w #16-1,d0 ; MAXIMAL 16 ROUTINEN
.EXEC: tst.l (a0)+ ; IST ADRESSE=0?
beq .CONT ; JA: WEITER
movea.l -4(a0),a1 ; SONST ADRESSE HOLEN
movem.l d0/a0,-(sp) ; SAVE REGISTER
jsr (a1) ; UND AUSFÜHREN
movem.l (sp)+,d0/a0 ; RESTORE REG.
.CONT: dbra d0,.EXEC ; NÄCHSTE ADR
movem.l (sp)+,d0-d7/a0-a6
rts ; ZURÜCK
EHANDLER:
; *** EIGENTLICHE ROUTINE, UM DIE ES GEHT... ***
SRT: bra.s .JMP ; ÜBERSPRINGEN
.DC.l "KUCK" ; KENNUNG...
.JMP: pea RESTXT(pc) ; TEXT
move.w #9,-(sp) ; F#9 : CCONWS
trap #1 ; GEMDOS
addq.l #6,sp ; STACKKORREKTUR
W_KEY: move.w #7,-(sp) ; FUNKTION #7
trap #1 ; GEMDOS: CRAWCIN
addq.l #2,sp ; STACKKORREKTUR
rts ; ZURÜCK
RESTXT: .DC.b 27,"p KUCKUCK! "
.DC.b 27,"q [TASTE]",13,10,0
.EVEN
ERT: ; ENDE DER ROUTINE
; *** ROUTINE KORRIGIERT PRÜFSUMME ***
CHCKSM: moveq.l #0,d0 ; D0 LÖSCHEN
movea.w #$600,a0 ; SEITE LADEN
move.w #256-2,d1 ; 255 WÖRTER TESTEN
.ADD: add.w (a0)+,d0 ; UND ADDIEREN
dbra d1,.ADD ; NÄCHSTES WORT
move.w #$5678,d2 ; PRÜFSUMME
sub.w d0,d2 ; AKT. PRÜFSUMME -
move.w d2,(a0)+ ; NEUE EINTRÄGEN
rts ; UND TSCHÜSS..
!*** DIESER TEIL BLEIBT NICHT RESIDENT! ***
SAVREGS:
MAIN: clr.l -(sp) ; MODUS: SV
move.w #32,-(sp) ; F# 32
trap #1 ; GEMDOS: SUPER
addq.l #6,sp ; STACKKORREKTUR
move.l d0, OLDSP ; ALTEN SSP MERKEN
lea.l $600,a0 ; BASISADRESSE
cmpi.l #$12123456,(a0) ; SCHON INST.?
bne .INST ; NEIN: INSTALLIEREN
cmpa.l 4(a0),a0 ; STIMMT DAS AUCH?
bne .INST ; NEIN: INSTALLIEREN
moveq.l #0,d1 ; LÖSCHEN (PRÜFSUMME)
move.w #$100-1,do ; 256 WÖRTER ADDIEREN
.TSTCHS: add.w (a0)+,d1 ; ADDIEREN
dbra d0,.TSTCHS ; NÄCHSTE
cmpi.w #$5678,d1 ; PRÜFSUMME KORREKT?
beq .JUMP ; JA: WEITER
.INST: bsr H_INST ; < EIGENER HANDLER >
bsr CHCKSM ; CHECKSUM-KORR.
bra .NOTFND ; KANN NOCH NICHT
; ; INSTALLIERT SEIN
.JUMP: ; TESTET, OB PPG SCHON RESIDENT
lea.l $680,a0 ; BASISADRESSE
moveq.l #32-1,d0 ; 32 SLOTS TESTEN
.TST: tst.l (a0)+ ; WERT=0?
beq .CONT1 ; ÜBERGEHEN
movea.l -4(a0),a1 ; SONST ADR HOLEN
cmpi.l #"KUCK",2(a1) ; STIMMT KENNUNG?
beq .FOUND ; JA: GEFUNDEN
.CONT1: dbra d0,.TST ; SONST: WEITERSUCHEN
cmpi.l #$31415926,$426 ; RESVALID?
bne .NOTFND ; NICHT GEFUNDEN!
movea.l $42a,a0 ; SONST ADR HOLEN
.TST2: cmpi.l #"XBRA",-12(a0) ; XBRA?
bne .NOTFND ; NEIN: >RAUS
cmpi.l #"KUCK",-8(a0) ; KENNUNG ?
beq .FOUND ; STIMMT: GEFUNDEN
tst.l -4(a0) ; IST ADR =0?
beq .NOTFND ; => LETZTE ROUTINE
movea.l -4(a0),a0 ; SONST ADR HOLEN
bra .TST2 ; UND WEITERSUCHEN
.NOTFND: cmpi.l #$31415926,$426 ; RESVALID?
bne .NORM ; NEIN: NORMAL WEITER
move.l $42a,OLDRV ; SONST: ADR. MERKEN
.NORM: move.l #RESVEC,$42a ; NEUE ROUT. INST,
move.l #$31415926,$426 ; RESVALID!
.K_RES: pea R_OK ; TXT:"PRG INST."
move.w #9,-(sp) ; F#=9
trap #1 ; GEMDOS: CCONWS
addq.l #6,sp ; STACKKORREKTUR
bsr W_KEY ; WARTE AUF TASTE
bsr SUPOFF ; SV-MODUS AUS
clr.w -(sp) ; STATUS: 0=OK
move.l #MAIN-START+1308,-(sp)
; SOVIELE BYTES RESIDENT HALTEN...
move.w #49,-(sp) ; F# 49
trap #1 ; GEMDOS
.FOUND: pea R_NI ; TXT:"SCHON INST."
move.w #9,-(sp) ; F#=9
trap #1 ; GEMDOS: CCONWS
addq.l #6,sp ; STACKKORREKTUR
bsr W_KEY ; WARTE AUF TASTE
bsr SUPOFF ; SV-MODUS AUS
clr.w -(sp) ; F#0
trap #1 ; GEMDOS: PTERM
; *** SUPERVISOR-MODUS AUS ***
SUPOFF: movea.l (sp)+,a3 ; RÜCKSPRUNGADR.
move.l OLDSP,-(sp) ; ALTER STACK,
move.w #32,-(sp) ; F# 32
trap #1 ; GEMDOS: SUPER
addq.l #6,sp ; STACKKORREKTUR
jmp (a3) ; UND ZURÜCK...
; *** INSTALLIERT HANDLER ***
H_INST: move.w #(EHANDLER-SHANDLER)/4,d0
; ; LÄNGE IN LONGS
movea.w #$600,a1 ; KOPIERZIEL
lea.l SHAMDLER,a0 ; STARTADR. QUELLE
.COPY1: move.l (a0)+,(a1)+ ; KOPIERE...
dbra d0,.COPY1 ; SOVIELE LANGWORTE
.CLR: cmpa.l #$800,a1 ; SCHON BEI $800?
beq .EXIT ; JA: EXIT
clr.l (a1)+ ; SONST LÖSCHEN
bra .CLR ; UND NOCH EINMAL
.EXIT: rts ; CIAO...
.DATA
R_OK: .DC.b "E KUCKUCK! ist jetzt"
.DC.b " resident.",13,10,0
R_NI: .DC.b "E KUCKUCK! war bereits"
.DC.b " installiert.",13,10,0
.EVEN
.BSS
OLDSP: .DS.L 1
; ###############################################
; ## *** R_KUCK! *** ##
; ## ENTFERNT KUCKUCK AUS DEM SPEICHER ##
; ## by ROLAND NITZ (c) 1992 MAXON Computer ##
; ###############################################
.ABS
KUCKLEN equ $200
.TEXT
START: clr.l -(sp) ; MODUS: SV
move.w #32,-(sp) ; F# 32
trap #1 ; GEMDOS: SUPER
addq.l #6,sp ; STACKKORREKTÜR
move.l d0,OLDSP ; ALTEN SSP MERKEN
lea.l $600,a0 ; BASISADRESSE
cmpi.l #$12123456,(a0) ; SCHON INST.?
bne .N_INST ; NEIN: INSTALLIEREN
cmpa.l 4(a0),a0 ; STIMMT DAS AUCH?
bne .N_INST ; NEIN: INSTALLIEREN
moveq.l #0,d1 ; LÖSCHEN (PRÜFSUMME)
move.w #$100-1,d0 ; 256 WÖRTER ADDIEREN
.TSTCHS: add.w (a0)+,d1 ; ADDIEREN
dbra d0,.TSTCHS ; NÄCHSTE
cmpi.w #$5678,d1 ; PRÜFSUMME KORREKT?
bne .N_INST ; JA: WEITER
lea.l $680,a0 ; BASISADRESSE
moveq.l #32-1,d0 ; 32 SLOTS TESTEN
.TST: tst.l (a0)+ ; WERT=0?
beq .CONT1 ; ÜBERGEHEN
movea.l -4(a0),a1 ; SONST ADR HOLEN
cmpi.l #"KUCK",2(a1) ; STIMMT KENNUNG?
beq .FOUND ; JA: GEFUNDEN
.CONT1: dbra d0,.TST ; SONST: WEITERSUCHEN
cmpi.l #$31415926,$426 ; RESVALID?
bne .N_INST ; NICHT GEFUNDEN!
lea.l $42a,a1 ; SONST BASIS HOLEN
.TST2: movea.l (a1),a0 ; ADR HOLEN
cmpi.l #"XBRA",-12(a0) ; XBRA?
bne .N_INST ; NEIN: >RAUS
cmpi.l #"KUCK",-8(a0) ; KENNUNG ?
beq .RVFOUND ; STIMMT: GEFUNDEN
tst.l -4(a0) ; IST ADR =0?
beq .N_INST ; => LETZTE ROUTINE
lea.l -4(a0),a1 ; SONST ADR HOLEN
bra .TST2 ; UND WEITERSUCHEN
.N_INST: pea NTEXT ; TXT: "WAR N. INST."
bra .PRGEX ; ->RAUS
.RVFOUND:
move.l -4(a0),(a1) ; IM RESVEC GEFUNDEN
tst.l $42a ; RES_VEC=0?
bne .REM ; NEIN: WEITER
clr.l $426 ; SONST RESVALID DEL.
bra .REM ; LÖSCHEN UND WEITER
.FOUND: lea.l -4(a0),a1 ; ZEIGER AUF ADRESSE
lea.l $680,a0 ; BASIS HOLEN
movea.l (a1),a2 ; ADRESSE HOLEN
movea.l $42e,a3 ; PHYSTOP HOLEN
movea.w #-1,a4 ; ADR.
moveq.l #32-1,d0 ; 32 SLOTS TESTEN
.GMIN: tst.l (a0)+ ; IST 0?
beq .CONT ; JA: WEITERSUCHEN
cmpa.l -4(a0),a4 ; KLEINER ALS ADR?
bls .JMP ; JA: WEITER
movea.l -4(a0),a4 ; SONST HOLEN
.JMP: cmpa.l -4(a0),a2 ; KLEINER ALS
bls .CONT ; A2? JA: WEITER
addi.l #KUCKLEN,-4(a0) ; ADR. KORR.
.CONT: dbra d0,.GMIN ; WEITERSUCHEN
clr.l (a1) ; EINTRAG LÖSCHEN
cmpa.l a4, a3 ; KLEINSTE ADR
bne .REMERR ; MUß=PHYSTOP SEIN
move.l a2,d0 ; ADRESSDIFFERENZ
sub.l a4,d0 ; ERRECHNEN,
beq .CNT2 ; IST =0? ->RAUS
subq.l #1,d0 ; WG. DBRA
.SHIFT: move.b -(a2),KUCKLEN(a2) ; SCHIEBE...
dbra d0,.SHIFT ; UND WEITER...
.CNT2: moveq.l #0,d0 ; DO LÖSCHEN
movea.w #$600,a0 ; SEITE LADEN
move.w #256-2,d1 ; 255 WÖRTER TESTEN
.ADD: add.W (a0)+,d0 ; UND ADDIEREN
dbra d1,.ADD ; NÄCHSTES WORT
move.w #$5678,d2 ; PRÜFSUMME
sub.w d0,d2 ; AKT. PRÜFSUMME -
move.w d2,(a0)+ ; NEUE EINTRAGEN
addi.l #KUCKLEN,$42e ; PHYSTOP KORR.
bra .REM ; EXIT ERFOLGSMELDUNG
.REMERR: pea FEHLER ; "FEHLER!!"
bra .PRGEX ; ->RAUS
.REM: pea REMOVED ; TXT:"PRG. ENTFERNT"
.PRGEX: move.w #9,-(sp) ; F#:9 CCONWS
trap #1 ; GEMDOS-CALL
addq.l #6,sp ; STACKKORREKTUR
move.w #7,-(sp) ; #7: CRAWCIN
trap #1 ; (GEMDOS)
addq.l #2,sp ; STACKKORREKTUR
bsr SUPOFF ; SV-MODUS AUS
clr.w -(sp) ; F#0: PTERM
trap #1 ; (GEMDOS)
; *** SUPERVISOR-MODUS AUS ***
SUPOFF: movea.l (sp)+,a3 ; RÜCKSPRUNGADR.
move.l OLDSP,-(sp) ; ALTER STACK
move.w #32,-(sp) ; F# 32
trap #1 ; GEMDOS: SUPER
addq.l #6,sp ; STACKKORREKTUR
jmp (a3) ; UND ZURÜCK...
.DATA
NTEXT: .DC.b "E KUCKUCK! konnte nicht"
.DC.b " gefunden werden.",13,10,0
FEHLER: .DC.b "E KUCKUCK! nur desaktiviert."
.DC.b " FEHLER: Speicher ungenutzt!! ",0
REMOVED: .DC.b "E KUCKUCK! wurde aus"
.DC.b " dem Speicher entfernt.",13,10,0
.EVEN
.BSS
OLDSP: .DS.l 1