← ST-Computer 05 / 1991

Der Trap-Trapper

Programmierpraxis

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