Der Trap-Trapper

Auf grösseren Systemen der Datenverarbeitung finden sich Teile des Betriebssystems, die über bestimmte Ereignisse eine Art Logbuch führen. Für derartige Programme wird üblicherweise der Begriff Logger verwendet, eine Standardanwendung ist z.B. das Führen einer Fehlerliste. Als Testhilfe kann das hier von mir vorgestellte Programm dienen, das die Betriebssystemaufrufe des ATARI ST über die Trap-Befehle des 68000-Prozessors protokollieren kann. Der Quellcode bietet für den Interessierten darüber hinaus einen gewissen Einblick in die Handhabung der Ausnahmebehandlungen Trace und Line-F bei diesem Prozessor.

Vor der Implementierung eines Loggers waren einige grundsätzliche Betrachtungen anzustellen, die ich vorab kurz darlegen möchte. Zunächst muß die Programmierung wegen der möglichst hohen Ausführungsgeschwindigkeit in Assembler erfolgen. Weil der Start einer solchen Anwendung meiner Meinung nach auch aus dem AUTO-Ordner heraus möglich sein sollte, entfällt - leider -eine Realisierung als Accessory. Darüber hinaus erscheint mir eine Beschränkung des Protokolls auf die drei Bereiche GEMDOS, AES und VDI als sinnvoll, da diese die mächtigsten Funktionen des Betriebssystems zur Verfügung stellen. Um die zu erzeugende Liste auch nach einem Systemabsturz mit sich anschließendem Kaltstart verfügbar zu haben, kann diese nicht im RAM, z.B. in einem Ringspeicher, geführt werden. Sie muß vielmehr dauerhaft abgelegt werden, deshalb entschied ich mich für die Ausgabe über einen am Centronics-Port anzuschließenden Drucker. Als Kennzeichnung eines Betriebssystemaufrufs genügt dafür ein Buchstabe (‘A’: AES, ‘G’: GEMDOS, ‘V’: VDI), kombiniert mit einer zweistelligen Zahl, die die Funktionsnummer hexadezimal beinhaltet. Eine auf diese Weise geführte Liste zieht allerdings, trotz der kryptischen Ausgabe, einen enormen Papierverbrauch nach sich und ist wohl auch in den wenigsten Fällen gewünscht. Deshalb mußte ich die Möglichkeit schaffen, den Ausdruck ein- bzw. auszuschalten. Zur Vereinfachung der Handhabung war zudem die Implementierung einer ebenfalls schaltbaren Zeitlupe notwendig. Auf die Steuerung dieser Funktionen über die Tastatur konnte ich verzichten, da die Möglichkeit besteht, die RS232-Schnittstelle als 2-Bit-PIO zu benutzen. Die Funktion des residenten Programms sollte außerdem durch weitere Aufrufe ausgesetzt bzw. wieder aktiviert werden können.

Die elementare Funktion des Loggers besteht darin, nach der Auslösung eines der beiden Traps die Kennung des entsprechenden Betriebssystemaufrufes auszudrucken. Diese Aufgabe kann dadurch gelöst werden, daß man die jeweils zugehörige Ausnahmebehandlung über eine entsprechende Routine umleitet. Geholt werden die relevanten Daten dann vom Stack (Trap 1) bzw. über ein Register (Trap 2). Wenn es doch nur so einfach wäre! Leider ist eine derartige Installation nicht von Dauer, das Betriebssystem des ST boykottiert ein Verbiegen des Traps 2 gelegentlich durch Überschreiben des Vektors mit einem Zeiger auf sich selbst, z.B. wenn man sich eine Textdatei über das Desktop anzeigen läßt. Vor den Erfolg setzten die Götter die Reise - durch den Speicher des ATARI ST. Nach Lösen mehrerer Fahrkarten bei verschiedenen Reiseveranstaltern bzw. Debuggern konnte ich die gesuchten Übeltäter finden. Verantwortlich für die Reinitialisierung des Vektors für Trap 2 sind Teile des GEM, die durch Auslösung der Line-F-Ausnahme aufgerufen werden. Diese Ausnahmebehandlung wird beim ATARI ST im allgemeinen Line-F-Emulator genannt. Sie wird vom Prozessor eingeleitet, sobald er erkennt, daß im nächsten auszuführenden Befehlswort - in diesem Zusammenhang mit Opcode bezeichnet - die vier höchsten Bits gesetzt sind. Von den Schöpfern des GEM wurde diese Eigenschaft des 68000 benutzt, um häufig verwendete Befehlsfolgen zu ersetzen, dazu gehören auch die Aufrufe der eben angesprochenen Routinen. Auf die Nachteile dieser Art von Programmierung wurde schon in mehreren Artikeln hingewiesen, für den Logger war sie aber durchaus nützlich. Die Arbeitsweise des Line-F-Emulators, beschränkt auf seinen für dieses Programm relevanten Teil (Listing 1), möchte ich kurz erläutern.

Der Line-F-Emulator führt zwei grundsätzlich verschiedene Funktionen aus. Ist das niedrigste Bit des Opcodes gesetzt, erfolgt die Freigabe eines Stack-Bereiches über Unlink (UNLK). Andernfalls werden die unteren drei Bytes des Opcodes - er muß glatt durch vier teilbar sein - als Offset in einer Tabelle von Startadressen diverser GEMRoutinen interpretiert. An der auf diese Weise festgelegten Stelle wird das Programm dann mit dem Ende der Ausnahmebehandlung fortgesetzt. Diese stellt vorher noch den Status des Prozessors wieder richtig. Außerdem legt sie die Wortadresse auf dem Stack ab, die dem Befehl folgt, der die Ausnahme einleitete. Bei entsprechender Programmierung kann eine Anwendung die in Frage kommenden Routinen mittels Trace feststellen und ihre unerwünschten Resultate umgehen. Bei einer Installation des so erweiterten Loggers aus dem AUTO-Ordner heraus verweigert das System jedoch beim Aufbau des Desktops unwiderruflich seine Mitarbeit. Bei genauerer Untersuchung stellte ich fest, daß nicht alle über den Line-F Emulator angesprungenen Unterprogramme im Trace-Modus durchlaufen werden können. Das Problem kann aber doch durch eine Kombination von Änderungen in den Routinen für Line-F, Trace und illegale Adresse gelöst werden. Dabei habe ich den Umstand genutzt, daß die hier interessierenden Unterprogramme mit einem Return-Befehl (RTS) abschließen. Die Ausnahmebehandlung für Line-F muß dann so erfolgen, daß bei Nutzung des Emulators als Sprungverteiler die erzeugte Rücksprungadresse illegal ist. Bei Beendigung einer auf diesem Wege angesprungenen Routine erkennt der Prozessor dann die illegale Adresse und kann den Vektor für Trap 2 gegebenenfalls wieder aufsetzen.

Genug der langen Einleitung, ich komme zur Sache (Listing 2). Der ausführbare Code beginnt wie üblich mit der Berechnung des Stackpointers und der Programmngröße, dann wird Trap 1 nachdem XBRA-Standard abgeklopft. Befindet sich das Programm schon resident im Rechner, wird sein Status geändert, und eine entsprechende Meldung erscheint auf dem Monitor. Andernfalls wird der Logger für GEMDOS eingerichtet, und das Programm bleibt nach Beendigung im Speicher. Der beim Rücksprung in das Betriebssystem ausgelöste Trap 1 führt sofort wieder in die Anwendung zum GEMDOS-Logger. Dieser besteht aus einem ersten Teil, der die Installation für Trap 2 besorgen soll, und einem zweiten, der die Protokollierung für Trap 1 erledigt. Sobald das entsprechende Bit in der Steuervariablen ein fertig installiertes Desktop anzeigt, wird der erste Teil übersprungen. Solange das aber, wie auch beim ersten Durchlauf, noch nicht zutrifft, testet das Programm, ob das Desktop läuft. Dabei wird davon ausgegangen, daß nach Ende der Boot-Phase unterschiedliche Ausnahmebehandlungen für Line-F und Trace existieren. Verläuft der Test negativ, wird bei den folgenden Aufrufen des GEMDOS geprüft, ob sowohl Line-F-Emulator als auch AES/VDI zwischenzeitlich eingebunden wurden. Sobald das Desktop dann läuft, wird der Merker gesetzt, und die Vektoren für illegale Adresse, Line-F-Emulator, Trace und Trap 2 werden geändert. Die Installation ist damit beendet, und die Aufrufe von AES und VDI werden ebenfalls über den Drucker aufgelistet.

Für die Ausgabe des Protokolls wird bei Trap 1 und auch Trap 2 dasselbe Unterprogramm benutzt. Es beinhaltet außerdem noch die Zeitlupe und läuft über das BIOS. Zur Steuerung habe ich die Eingangssignale CTS (clear to send) und CD (carrier detect) bzw. die Ausgangssignale RTS (request to send) und DTR (data terminal ready) der seriellen Schnittstelle (Tabelle 1) vorgesehen. An Elektrikteilen braucht man einen 25poligen Stecker, ein Stück 4poliges Kabel und zwei Schalter. Die Teile sind jetzt so zu verlöten, daß mit dem einen Schalter CTS und RTS, mit dem anderen CD und DTR verbunden bzw. getrennt werden können. Am Parallel-Port des MFP 68901 kann dann die Schalterstellung gelesen werden.

014A74 MOVE.W   (A7)+,D2        ; hole Kopie des alten Status'
014A76 MOVE.L   (A7)+,A0        ; und Opcode (FXXX), Rücksprung-
014A78 MOVE.W   (A0)+,D1        ; adresse jetzt in A0,
014A7A BTST     #0,D1           ; Opcode testen, gegebenenfalls
014A7E BNE      $014A9A         ; UNLK-Funktion (...) ausführen,
014A80 MOVE.W   D2,SR           ; sonst Status restaurieren,
014A82 MOVE.L   A0,-(A7)        ; Rücksprungadresse auf Stack,
014A84 ANDI.W   #$0FFF,D1       ; Adresse der
014A88 MOVE.L   #$00FEE56A,A0   ; gewünschten Routine aus
014A8E MOVE.L   $00(A0,D1.W),A0 ; der Tabelle holen und
014A92 JMP      (A0)            ; anspringen  
014A94 ...                      ; ab hier Unlink-Funktion

Listing 1: Line-F-Emulator

Pin Bezeichnung
1 Gnd (ground)
2 TxD (transmit data)
3 RxD (receive data)
4 RTS (request to send)
5 CTS (clear to send)
7 Gnd (signal ground)
8 CD (carrier detect)
20 DTR (data terminal ready)
22 Rl (ring indicator)

Tabelle 1: Belegung der RS232-Schnittstelle

Was passiert nun, wenn das Betriebssystem den Line-F-Emulator aufruft? Die modifizierte Ausnahmebehandlung prüft zunächst das niedrigste Bit des Opcodes, denn wenn der Line-F-Emulators im Unlink-Modus benutzt werden soll, kann ja ab hier wie bisher verfahren werden. Ansonsten wird der dem aktuellen Opcode-Wert entsprechende, beim Start des Loggers mit $FF vorbesetzte Merker getestet. Beinhaltet der Merker den Wert $00, so kann eine Reinitialisierung des Vektors für Trap 2 durch die zuzuordnende Routine ausgeschlossen werden; ohne Manipulationen läuft die Ausnahmebehandlung dann weiter über den Line-F-Emulator des GEM. Andernfalls wird die analog zur Vorgehens weise des Vorbildes erzeugte Rücksprungadresse auf dem Supervisor-Stack abgelegt. Anschließend wird in der Kopie des auf dem Stack abgelegten System-Bytes das Tracebit gesetzt. Schließlich kann über den gespeicherten alten Vektor der Sprung zum Original erfolgen. Hier weicht der Lauf der Dinge nur dann vom bisher bekannten ab, wenn die Kopie des Statuswortes, wie beschrieben, geändert wurde. Sobald dieses vom Stack geholt wird, um den Prozessorstatus zu restaurieren, wird der Trace-Modus eingeschaltet. Das Trace-Programm prüft ab jetzt nach jedem vom Line-F-Emulator ausgeführten Befehl, ob der auf dem entsprechenden Stack befindliche Eintrag identisch ist mit dem vorher abgelegten Rücksprungziel. Sobald dieser Zustand erreicht ist, wird das niedrigste Bit der weiterhin maßgeblichen Adresse gesetzt, die jetzt überzählige auf dem Supervisor-Stack gelöscht. Dann erfolgt die Beendigung des Trace-Modus’, und der Aufruf der gewünschten Routine schließt sich an. Nach deren Abarbeitung liest der Prozessor dann einen ungeraden Wert als Rücksprungadresse, also eine illegale Adresse. In der hierdurch ausgelösten Ausnahmebehandlung wird zuerst überprüft, ob sie durch eine Aktion des Loggers ausgelöst wurde, wenn nicht, hagelt es Bomben wie üblich. Ansonsten berichtigt die Routine entweder den Vektor für Trap 2, oder der dem soeben beendeten GEM-Teil zugeordnete Merker wird gelöscht. Dann wird das Programm an der richtigen Adresse fortgesetzt.

Zum Schluß gebe ich noch einige Anregungen zur eventuell nötigen Anpassung des Loggers an eine andere als die vorgesehene Konfiguration. Ein serieller Drucker kann relativ einfach angeschlossen werden, indem man die Stellung der Schalter über den Centronics-Port einliest. Diese Abfrage kann natürlich auch mit Hilfe einer I/O-Karte erfolgen, aber die hat ja nun mal nicht jeder. Obwohl von mir nur mit RAM-TOS bzw. Blitter-TOS auf einem Mega 1 entwickelt und getestet, habe ich die Hoffnung, daß das Programm auch mit den anderen Versionen des Betriebssystems läuft. Der kritische Punkt ist sicherlich der Test des Desktops. Das gilt vor allem für den neuen 1040 STE. Auf ihm, wie auch anderen Derivaten und zukünftigen Versionen des Betriebssystems (TOS030, PAK-68K, ...), die ohne Line-F-Emulator auskommen, kann der Logger in dieser Form nicht zum Laufen gebracht werden. Das ist der Preis, den man für eine solche Programmierung zahlen muß ... Aber ich weiß jetzt, was die Kiste beim Booten macht!

Literatur:

[1] Service Manual Mega 1, ATARI Corp.

[2] Brückmann, Englisch, Gerits, ATARI ST Intern. Data Becker GmbH Düsseldorf

;************************************************ 
;*      TRAP_LOG.S - Stephan Simson             *
;*      (c) MAXON Computer GmbH 1991            *
;*      Versionen                               *
;*      0.0     31.10.89: GEMDOS-Logger         *
;*      1.0     10.12.89: AES/VDI-Logger        *
;*      1.1     23.01.90: GEMDOS-Logger nur     *
;*                        mit A0, D0            *
;*      2.0     27.03.90: AUTO-Start, XBRA      *
;************************************************

;************************************************ 
;*      Zuweisungen                             *
;************************************************ 
;
;       Base-Page-Offset-Werte
TxtSgSiz EQU    $00C    ; Text-Segment-Größe
DatSgSiz EQU    $014    ; Daten-Segment-Größe
BssSgSiz EQU    $01C    ; BSS-Segment-Größe
BasPgSiz EQU    $100    ; Base-Page-Größe
;
;       Ascii-Zeichen
;
NUL      EQU    $00
LF       EQU    $0A
CR       EQU    $0D
BLANK    EQU    $20
;
;       Hardware-Adressen
;
MFP_PIO  EQU    $FFFA01 ; MFP 68901: par. I/O
;
;       GEMDOS-Funktionen
;
CRAWCIN  EQU    $07     ; rohe Zeicheneingabe
CCONWS   EQU    $09     ; Stringausgabe
PTERM0   EQU    $00     ; Programmende
PTERMRES EQU    $31     ; Programmende / res.
;
;       BIOS-Funktionen
;
SetExec  EQU    $05     ; Exc.-Vektor setzen
Bconout  EQU    $03     ; Ausgabe Zeichen
Bcostat  EQU    $08     ; Test Status Ausgabe
;
;       Vektor-Nummern
;
IllAdVNr EQU    $03     ; illegale Adresse
TraceVNr EQU    $09     ; Trace
FLineVNr EQU    $0B     ; Line-F
T01VktNr EQU    $21     ; Trap-01 (GEMDOS)
T02VktNr EQU    $22     ; Trap-02 (AES / VDI)
;
;       Trap-Nummern
;
GEMDOS   EQU    $01     ;
BIOS     EQU    $0D     ;
;
;       Geräte
;
PRN      EQU    0       ; Centronics-Port
;
;       Bits
;
Unlink   EQU    0       ; FLine:Unlink-Modus
Gesperrt EQU    0       ; Funktion gesperrt
Drucken  EQU    1       ; carrier detect
Zeitlupe EQU    2       ; clear to send
Desktop  EQU    3       ; Desktop installiert
BootTest EQU    4       ; Boot-Test gelaufen
SuperMod EQU    5       ; Supervisor-Modus
TraceMod EQU    7       ; Trace-Modus
;
;       Trap-2 Funktionen
;
AES_Code EQU    $C8     ; AES-Funktion
VDI_Code EQU    $73     ; VDI-Funktion
;
;       Sonstige
;
StckSize EQU    $100    ; Stack-Größe: 256
Bereit   EQU    $FFFF   ; Geräte-Status

;************************************************ 
;*      Programm                                *
;************************************************ 

    TEXT 

ProgrBgn:
;
;       Berechnung: RAM-Bedarf und Stack-Pointer
;
    LEA.L   ProgrBgn-BasPgSiz(PC),SP
                            ; SP -> BP 
    MOVE.L  DatSgSiz(SP),D7 ; Data-Seg.
    ADD.L   BssSgSiz(SP),D7 ; BSS-Seg.
    ADD.L   #BasPgSiz,D7    ; Base-Page 
    ADD.L   TxtSgSiz(SP),D7 ; Text-Seg.
    ADD.L   #StckSize,D7    ; Stack-Gr.
    OR.B    #$01,D7         ; D7: RAM-
    ADDQ    #$01,D7         ; Bedarf
    ADD.L   D7,SP           ; SP okay
;
;       Test der existierenden Trap-01-Routinen
;
    MOVE.L  #-1,-(SP)       ; hole
    MOVE.W  #T01VktNr,-(SP) ; alten
    MOVE.W  #SetExec,-(SP)  ; Vektor
    TRAP    #BIOS           ; nach D0
    ADDQ.L  #8,SP           ;
    AND.L   #$ 00FFFFFF,D0  ;
    MOVE.L  D0,A4           ;
SuchXBRA:       CMP.L #'XBRA',-12(A4) ; nach
    BNE     InstaLOG        ; XBRA
    CMP.L   #'TLOG',-8(A4)  ; Standard
    BEQ     StatAend        ; vorgehen
    MOVE.L  -4(A4),A4       ;
    BRA     SuchXBRA        ;
;
;       Status ändern
;
StatAend:       BCHG #Gesperrt,-14(A4) ; ge-
    BEQ     NichtAkt        ; sperrt ?
    LEA     Meldung2(PC),A0 ; Freigabe-
    BRA     ZeigStat        ; oder

NichtAkt:       LEA Meldung3(PC),A0 ; Sperr-
ZeigStat:       BSR ZeigMeld    ; Meldung
    MOVE.W  #PTERM0,-(SP)   ; Programm 
    TRAP    #GEMDOS         ; beenden
;
;       Installation des Loggers für Trap-01
;
InstaLOG:       PEA Trp01LOG    ; Routine
    MOVE.W  #T01VktNr,-(SP) ; für 
    MOVE.W  #SetExec,-(SP)  ; Trap-01 
    TRAP    #BIOS           ; einbauen
    MOVE.L  D0,_Trp01OV     ;
    ADDQ.L  #8,SP           ;

    LEA     Meldung1(PC),A0 ; Install.
    BSR     ZeigMeld        ; melden

    MOVE.W  #0,(SP)         ; Programm
    MOVE.L  D7,-(SP)        ; beenden &
    MOVE.W  #PTERMRES,-(SP) ; resident 
    TRAP    #GEMDOS         ; halten
;
;       Meldung zeigen und Taste warten
;
;       Aufruf
;       <A0>: Zeiger auf Meldungstext
;
ZeigMeld:       MOVE.L A0,-(SP)
    MOVE.W  #CCONWS,-(SP)   ; Meldung 
    TRAP    #GEMDOS         ; ausgeben
    ADD.L   #6,SP           ;
    MOVE.W  #CRAWCIN,-(SP)  ; warte auf 
    TRAP    #GEMDOS         ; Taste
    ADD.L   #2,SP           ;
    RTS                     ;
;
;       Trap 01 TRAP_LOG.TOS
;
;       benutzte Register: A0, D0
;
StrngFlg:       DC.W    NUL     ; Steuerung
Magic_01:       DC.B    'XBRA'  ; Magic
Ident_01:       DC.B    'TLOG'  ; Ident-Nr.
_Trp010V:       DC.L    0       ; Original

Trp01LOG:       BTST    #Desktop,StrngFlg ; ggf.
    BNE     Tr01LOG1            ; Trap-02-
    BSR     TestBoot            ; Logger
    BTST    #Desktop, StrngFlg  ; auch noch
    BEQ     Tr01LOG1            ; instal—
    BSR     InstTr02            ; lieren
Tr01LOG1:       MOVE.L  SP,A0   ; A0 = SP
    BTST    #SuperMod,(SP)      ; alter 
    BNE     Tr01LOG2            ; Status
    MOVE.L  USP,SP              ; bestimmt,
    MOVE.L  SP,D0               ; ob USP zu
    BRA     Tr01LOG3            ; benutzen

Tr01LOG2:       MOVE.L  SP,D0   ; ist oder
    ADDQ.L  #6,D0               ; SSP
Tr01LOG3:       EXG     D0,A0   ; rette den
    MOVE.L  D0,-(SP)            ; alten SP
    BTST    #Gesperrt,StrngFlg  ; ge-
    BNE     Tr01LOG4            ; sperrt ?
    MOVE.L  A0,-(SP)
    MOVE.W  #'G',D0             ; 'G' für
    BSR     AusgZchn            ; GEMDOS,
    MOVE.L  (SP)+,A0            ; dann über
    MOVE.W  (A0),D0             ; A0 adres-
    MOVE.B  D0,-(SP)            ; sierte
    ASR.B   #4,D0               ; Nummer
    BSR     AusgHlbB            ; ausgeben
    MOVE.B  (SP)+,D0            ;
    BSR     AusgHlbB            ;
    BSR     AusgLeer            ; 
Tr01LOG4:       MOVE.L (SP),SP      ; SP okay
Tr01LOG5:       MOVE.L _Trp01OV,A0  ; weiter
    JMP     (A0)                    ; wie sonst
;
; Installation Trap-02-Logger
;
InstTr02:       BSR RettOrgV    ; erst die
    LEA     IllAdLOG(PC),A0     ; Original-
    MOVE.L  A0,4*IllAdVNr       ; Vektoren
    LEA     TraceLOG(PC),A0     ; sichern,
    MOVE.L  A0,4*TraceVNr       ; dann die
    LEA     FLineLOG(PC),A0     ; Logger-
    MOVE.L  A0,4*FLineVNr       ; Routinen 
    LEA     Trp02LOG(PC),A0     ; ein-
    MOVE.L  A0,4*T02VktNr       ; binden
    RTS                         ;
;
;       Rettung der Original-Vektoren
;
RettOrgV:       MOVE.L 4*IllAdVNr,_IllAdOV ;
    MOVE.L  4*TraceVNr,_TraceOV     ;
    MOVE.L  4*FLineVNr,_FLineOV     ;
    MOVE.L  4*T02VktNr,_Trp02OV     ;
    RTS                     ;
;
;       Test, ob das Boot schon schwimmt
;
TestBoot: BSET #BootTest,StrngFlg   ; Merker f.
    BNE     TstBoot2            ; TestBoot
    MOVE.L  4*FLineVNr,-(SP)    ; wenn 
    OR.L    #$FF000000,(SP)     ; FLine- &
    MOVE.L  4*TraceVNr,D0       ; Trace-
    OR.L    #$FF000000,D0       ; Vektor
    CMP.L   (SP)+,D0            ; ungleich,
    BEQ     TstBoot1            ; dann
    BSET    #Desktop,StrngFlg   ; läuft
    RTS                         ; Desktop

TstBoot1:       BSR RettOrgV    ; Vektoren
    RTS                         ; retten

TstBoot2:       MOVE.L 4*FLineVNr,D0 ; Desktop
    CMP.L   _FLineOV,D0         ; läuft,
    BEQ     TstBoot9            ; wenn
    MOVE.L  4*T02VktNr,D0       ; Vektoren
    CMP.L   _Trp02OV,D0         ; für FLine
    BEQ     TstBoot9            ; & Trap-02
    BSET    #Desktop,StrngFlg   ; geändert
TstBoot9:       RTS             ;
;
;       Trap 02 TRAP_LOG.TOS
;
;       geänderte Register: keine
;
Magic_02:       DC.B    'XBRA'  ; Magic
Ident_02:       DC.B    'TLOG'  ; Ident-Nr.
_Trp02OV:       DC.L    0       ; Original

Trp02LOG: BTST #Gesperrt,StrngFlg ; ge-
    BNE     Tr02LOG6            ; sperrt ?
    MOVEM.L D0-D2/A0-A2,-(SP)   ; rette
    MOVE.L  D1,-(SP)            ; Register
    CMP.W   #AES_Code,D0        ; AES-
    BEQ     Trp02AES            ; Aufruf ?
    CMP.W   #VDI_Code,D0        ; VDI-
    BEQ     Trp02VDI            ; Aufruf ?
    ADDQ.L  #4,SP               ; wer kommt
    BRA     Tr02LOG5            ; hierhin ?

Trp02VDI:       MOVE.W #'V',D0  ; 'V' / 'A'
    BRA     Tr02LOG4            ; für

Trp02AES:       MOVE.W #'A',D0  ; VDI / AES
Tr02LOG4:       BSR     AusgZchn ; ausgeben
    MOVE.L  (SP)+,A0            ; zeigt auf
    MOVE.L  (A0),A0             ; Zeiger,
    MOVE.W  (A0),D0             ; der zeigt
    BSR     AusgByte            ; auf Nr.
    BSR     AusgLeer            ;

Tr02LOG5:   MOVEM.L (SP)+,D0-D2/A0-A2 ; Reg. okay
Tr02LOG6:   MOVE.L  _Trp02OV,-(SP)    ; weiter,
    RTS                         ; wie sonst
;
;       illegale Adresse
;
_IllAdOV:       DC.L    NUL     ; Original

IllAdLOG:       MOVE.L  A0,-(SP) ; A0 und D0
    MOVE.L  D0,-(SP)            ; retten
    MOVE.L  10(SP),A0           ; ill. Adr.
    CMP.B   #$F0,-3(A0)         ; selbst
    BLT     IllAdAlt            ; erzeugt ?
    MOVE.L  4*T02VktNr,D0       ; Logger
    CMP.L   _Trp02OV,D0         ; wieder
    BNE     IlAdLOG1            ; einbin-
    LEA     Trp02LOG(PC),A0     ; den, wenn
    MOVE.L  A0,4*T02VktNr       ; Vektor
    BRA     IlAdLOG2            ; verändert

IlAdLOG1:       MOVE.W -3(A0),D0 ; sonst
    AND.L   #$00000FFF,D0       ; Merker
    ASR.W   #2,D0               ; für zuge
    EXG.L   D0,A0               ; hörige
    ADD.L   #FLineTab,A0        ; Routine
    CLR.B   (A0)                ; löschen

IlAdLOG2:       MOVE.L (SP)+,D0 ; A0, D0
    MOVE.L  (SP)+,A0            ; Stack und
    MOVE.L  2(SP),10 (SP)       ; Rück-
    AND.L   #$FFFFFFFE,10(SP)   ; sprung
    ADD.L   #8,SP               ; adresse
    RTE                         ; okay

IllAdAlt:       MOVE.L (SP)+,D0 ; D0 & A0
    MOVE.L  (SP)+,A0            ; okay,
    MOVE.L  _IllAdOV,A0         ; weiter
    JMP     (A0)                ; wie sonst
;
;       FLine
;
_FLineOV:       DC.L NUL        ; Original

FLineLOG:       MOVE.L A0,-(SP) ; A0 retten
    MOVE.L  6(SP),A0            ; Opcode
    ADD.L   #1,A0               ; testen
    BTST    #Unlink,(A0)        ;
    BNE     FLinLOG1            ;
    MOVE L  D0,-(SP)            ; D0 retten
    ADD.L   #1,A0               ; Nummer d.
    MOVE.W  -2(A0),D0           ; FLine-
    AND.L   #$00000FFF,D0       ; Routine
    ASR.H   #2,D0               ; bestimmen
    EXG.L   D0,A0               ; und zuge-
    ADD.L   #FLineTab,A0        ; hörigen
    TST.B   (A0)                ; Merker
    BEQ     FLinLOGO            ; testen,
    MOVE.L  (SP),-(SP)          ; ggf.
    MOVE.L  8(SP),4(SP)         ; Adresse
    MOVE.W  12(SP),8(SP)        ; merken &
    MOVE.L  14(SP),10(SP)       ; und Trace
    MOVE.L  D0,14(SP)           ; ver-
    BSET    #TraceMod,8(SP)     ; anlassen
FLinLOG0:       MOVE.L (SP)+,D0 ; D0 und A0
FLinLOG1:       MOVE.L (SP),A0  ; okay
    MOVE.L  _FLineOV,(SP)       ; weiter
    RTS                         ; wie sonst
;
;       Trace
;
_TraceOV:       DC.L NUL        ; Original

TraceLOG:       MOVE.L A0,-(SP) ; A0 & D0
    MOVE.L  D0,-(SP)            ; retten
    BTST    #SuperMod,8(SP)     ; alter
    BEQ     TracLOG1            ; Status
    MOVE.L  SP,A0               ; bestimmt,
    ADD.L   #18,A0              ; ob SSP
    BRA     TracLOG2            ; oder USP

TracLOG1:       MOVE.L USP,A0   ; zu nutzen
TracLOG2:       MOVE.L (A0),D0  ; wenn Adr.
    CMP.L   14(SP),D0           ; ungleich,
    BEQ     TraceEnd            ; D0 & A0
    MOVE.L  (SP)+,D0            ; vom Stack
    MOVE.L  (SP)+,A0            ; holen,
    RTE                         ; tracen

TraceEnd:       BSET #0,3(A0)   ; Adresse,
    MOVE.L  (SP)+,D0            ; D0, A0
    MOVE.L  (SP)+,A0            ; und Stack
    MOVE.L  2(SP),6(SP)         ; berichti-
    MOVE.W  (SP),4(SP)          ; gen,
    BCLR    #TraceMod,4(SP)     ; Trace
    ADD.L   #4,SP               ; beenden
    RTE                         ;
;
;       Ausgabe Byte
;
AusgByte:       MOVE.W D0,-(SP) ;
    ASR.B   #4,D0               ; oberes
    BSR     AusgHlbB            ; Halbbyte
    MOVE.W  (SP)+,D0            ; unteres
    BSR     AusgHlbB            ; Halbbyte
    RTS
;
;       Ausgabe Halb-Byte (nibble)
;
AusgHlbB:       AND.W #$000F,D0 ; unterstes
    LEA     HexAscTb(PC),A0     ; Halbbyte
    MOVE.B  (A0,D0),D0          ; zeigt auf
    BRA     AusgZchn            ; Zeichen
;
;       Neue Zeile
;
NeueZeil:       MOVE.W #CR,D0   ; Zeilen-
    BSR     AusgZchn            ; anfang
    MOVE.W  #LF,D0              ; nächste
    BRA     AusgZchn            ; Zeile
;
;       Ausgabe Leerzeichen
;
AusgLeer:       MOVE.W #BLANK,D0 ; ' ' !
;
;       Ausgabe Zeichen
;
;       enthalten sind Start/Stop und Zeitlupe
;
;       Aufruf
;
;       <D0>: Zeichen
;
AusgZchn:       MOVEM.L D1-D2/A1-A2,-(SP) ; Register 
    MOVE.W  D0,-(SP)            ; retten
    BTST    #Zeitlupe,MFP_PIO   ; Tee
    BEQ     AusgZch1            ; trinken,
    MOVE.L  #150,D0             ; wenn ge-
    BSR     Wartzeit            ; schlossen
AusgZch1:       BSR TstDruck    ; zurück,
    BNE     AusgZch3            ; wenn
    ADD.L   #2,SP               ; geöffnet
    BRA     AusgZch4            ;

AusgZch3:       MOVE.W #PRN,-(SP) ; Status
    MOVE.W  #Bcostat,-(SP)      ; des
    TRAP    #BIOS               ; Druckers
    ADDQ.L  #4,SP               ; ermitteln

    CMP.W   #Bereit,D0          ; wenn
    BNE     AusgZch1            ; bereit,
    MOVE.W  #PRN,—(SP)          ; dann
    MOVE.W  #Bconout,-(SP)      ; Zeichen
    TRAP    #BIOS               ; ausgeben
    ADDQ.L  #6,SP               ;
AusgZch4:       MOVEM.L (SP)+,D1-D2/A1-A2 ; Register 
    RTS                         ; holen
;
;       Wartezeit
;
;       Aufruf
;       <D0>:   Anzahl der Wartezyklen,
;               leider ein Software-Timer
;
Wartzeit:       MOVE.L #10,D1
Wartzeil:       DBRA   D1,Wartzeil
    DBRA    D0,Wartzeit
    RTS                         ; genug
;
;       Test Schalter 'Drucken'
;
;       Rücksprung
;       <Z>:    1 - Druck
;               0 - kein Druck
;
TstDruck:       MOVE.L D1,-(SP) ; D1 retten
    MOVE.B  MFP_PIO,D1          ; Wenn
    MOVE.B  StrngFlg,D0         ; Schalter
    EOR.B   D1,D0               ; betätigt,
    BTST    #Drucken,D0         ; dann Flag
    BEQ     TstDrck2            ; ändern &
    BCHG    #Drucken,StrngFlg   ; ggf.
    BNE     TstDrck1            ; Zeilen-
    BSR     NeueZeil            ; vorschub
TstDrck1:       MOVE.L #1000,D0 ; Ent-
    BSR     Wartzeit            ; prellen
TstDrck2:       MOVE.L (SP)+,D1 ; D1 okay
    BTST    #Drucken,StrngFlg   ; Test
    RTS
;
;       Konstanten
;
HexAscTb:       DC.B '0123456789ABCDEF' ; Hex-Ascii
;
; Variablen
;
FLineTab:                       ; Merker
REPT    $1000/4                 ; für
    DC.B    $FF                 ; Trace
ENDM
;
;       Texte
;
Meldung1:   DC.B 'TRAP_LOG.PRG installiert'
    DC.B CR,LF,LF
    DC.B ' Stephan Simson 27.03.90'
    DC.B CR,LF,NUL

Meldung2: DC.B 'TRAP_LOG.PRG wieder aktiv'
    DC.B  CR,LF,NUL

Meldung3: DC.B 'TRAP_LOG.PRG deaktiviert'
    DC.B  CR,LF,NUL

END

Stephan Simson
Aus: ST-Computer 05 / 1991, Seite 81

Links

Copyright-Bestimmungen: siehe Über diese Seite