Ein im AUTO-Ordner abgelegtes Programm eliminiert die Fehler des RTS/CTS-Handshakes sowohl in der PROM- als auch in Disketten-Version der bisherigen TOS-Versionen.
Für viele Besitzer eines Druckers mit ausschließlich seriellem Anschluß stellen die BIOS-Fehler in der Behandlung der XON/XOFF- und RTS/ CTS-Handshake-Signale des seriellen Ports ein Ärgernis dar. Wenngleich (fast) alle Textprogramme hier hilfreich eingreifen und einen fehlerfreien Treiber installieren, so ist dieser nur für die Laufzeit der Programme vorhanden. Ein Anklicken eines Textfiles unter GEM-Desktop und die Auswahl „DRUCKEN“ endet jedesmal mit einer Endlos-Schleife in der fehlerhaften Interruptroutine, nur erneutes Booten erweckt den ST wieder zum Leben.
Das kann man zwar umgehen, indem kein Handshake-Protokoll des seriellen Ports aktiviert wird (RS232-Einstellungs-ACC), dafür fehlen dann aber auch garantiert Zeichen auf dem Druckerpapier. Die Gründe für dieses Fehlverhalten des seriellen Ports sind wohl dem Umstand zuzuschreiben, daß ATARI diesen Port 'Modem’ nennt und ihn wohl auch nur in dieser Umgebung ausgetestet hat. Alle seriellen Kopplungen, sei es über ein Modem zu einem anderen Rechner oder nur die lokale Kopplung mit einem Drucker, müssen dem Partner mitteilen, wann sie bereit sind, Daten zu empfangen, damit der (serielle) Datenstrom nicht im Nirwana endet. Zu diesem Zweck gibt es die zwei gebräuchlichsten Verfahren XON/XOFF und RTS/CTS.
Im abgedruckten Assemblerlisting sind die Fehler des RTS/CTS-Protokolls behoben. Dieses Protokoll wird auch Hardware-Handshake genannt, da sich die beiden gekoppelten Geräte über zwei zusätzliche Leitungen RTS (request to send) und CTS (clear to send) über die Sende- bzw. Empfangsbereitschaft unterhalten. Bei der Ankopplung eines Druckers spielt nur das Signal CTS eine Rolle, hierrüber teilt der Drucker dem ST mit, wann er in der Lage ist, Zeichen zu empfangen. Dieses Signal wird im ST vom Pin 5 des Steckers der seriellen Schnittstelle auf den Interrupt-Eingang 2 des MFP 68901 (Multifunktionsbaustein) geführt. Ein Pegelwechsel an diesem Eingang führt zu einem Interrupt, der den Prozessor veranlasst, eine mittels Interruptvektor diesem Interrupt zugeordnete Routine auszuführen. Ob eine positive oder negative Flanke diesen Interrupt auslöst, muß im ’active edge register’ des MFP programmiert werden. Ein solcher Interrupt aktiviert die CTS-Interruptroutine. Wird hingegen ein Zeichen gesendet, ebenfalls durch diesen MFP-Baustein, so teilt das Sen-deregister des MEP dem 68000 mittels Interrupt mit, wann es wieder bereit zur Aufnahme eines weiteren zu sendenden Zeichens ist. Dieser Interrupt aktiviert die Sende-Interruptroutine. In der Sende-Interruptroutine muß nun geprüft werden, ob ein fehlendes CTS-Signal vom Drucker die weitere Aussendung von Zeichen verhindert.
Fehlt das CTS-Signal, so darf kein weiteres Zeichen in das Senderegister abgelegt werden, es muß vielmehr auf die Flanke eines wiederkehrenden CTS-Signals gewartet werden. Mittels Disassembler, in diesem Fall mit dem PROFIMAT von DATA BECKER, wurden die entsprechenden Interruptroutinen analysiert und die Fehlerursachen lokalisiert.
In der Sende-Routine verläßt sich der ATARI darauf, daß nach jedem Zeichen das CTS-Signal geht und anschließend (Interrupt-auslösend) wiederkommt. Drucker verhalten sich hier aber anders, sie lassen das CTS-Signal solange gesetzt, bis der interne Puffer voll ist und nehmen dann erst das Signal weg. In der Sende-Interruptroutine muß also das statisch anliegende CTS-Signal überprüft werden; ist es vorhanden, muß ein weiteres Zeichen gesendet werden. Erst ein Fehlen des Signals ist Anlaß, das Zeichensenden zu unterbinden und sich wieder durch einen CTS-Interrupt wecken zu lassen.
In der CTS-Interruptroutine ist ebenfalls ein Fehler: Hier wird abgefragt, ob das Senderegister des MFP bereit zur Aufnahme eines neuen Zeichens ist. Ist es noch nicht bereit, wird in einer Schleife ständig die Sendebereitschaft überprüft. Leider stimmt das Sprungziel in der Schleife nicht, denn es wird immer nur der gerettete Wert des XMIT-Statusregisters abgefragt. Hier wird sich aber nie wieder etwas ändern, und schon ist <man in der schönsten Endlosschleife.
Nachdem die Fehler erkannt waren, wurde eine Möglichkeit zur Behebung gesucht. Da das TOS im PROM war, hätte man neue EPROMS schießen können, was aber ziemlich umständlich, zumal es elegantere Möglichkeiten gibt, die Fehler zu beheben. Es bietet sich an, die betroffenen Interruptroutinen neu zu programmieren und mit einem im AUTO-Ordner abgelegten Programm nach jedem Bootvorgang neu zu installieren. TOS bietet die Möglichkeit, ein Programm resident im Speicher zu belassen, ein Verfahren, das z. B. alle Druckerspooler nutzen. Da die neuen Interruptroutinen sowohl die Fehler im PROM-TOS als auch im Disketten-TOS beheben sollen, muß man sich alle benötigten absoluten Adressen mit BIOS-Aufrufe besorgen.
Hier eine kurze Beschreibung der Vorgehensweise: Zuerst wird in bekannter Weise die Länge des Programms berechnet, dieser Parameter wird beim Verlassen des Programmes mit dem GEM KEEP-Call benötigt. Nach dieser Berechnung wird die Adresse des Puffers geholt, in dem die Sendedaten für die serielle Schnittstelle bereitgestellt werden. Dazu dient der IOREC-XBIOS-Call, dieser liefert in DO einen Pointer auf den Ausgabepuffer zurück. Nach dieser Berechnung wird der Vektor des Sende-Bereit-Interrup-tes (XREADY) auf die neue Interruptroutine umgelegt. Dies geschieht mit dem MFPINT-XBIOS-Call (13). In gleicher Weise wird der Vektor des CTS-Interruptes auf die neue CTS-Interruptroutine umgelegt. Danach kann das Programm mittels KEEP-Call verlassen und zum Desktop zurückgekehrt werden. Die geladenen neuen Interruptroutinen stehen jetzt permanent im Speicher und erleichtern dem Besitzer eines seriell angeschlossenen Druckers das Leben un-gemein. Wenn das Programm dann noch in einen Autoordner geladen wird, können die Probleme (bis zur nächsten TOS-Version) vergessen werden.
Für diejenigen, die keinen Assembler besitzen, ist ein BASIC-Lader abgedruckt.
R. Lange
10 ' V24PATCH.PRG Lader
11 Filename$="V24PATCH.PRG"
12 OPEN "O",1,Filename$
13 READ Wert
14 REPEAT
15 PRINT #1, CHR$(Wert);
16 Summe=Summe+Wert
17 READ Wert
18 UNTIL Wert=-1
19 READ Pruefsumme
20 IF Pruefsumme<>Summe THEN
21 PRINT "Fehler In Datas"
22 ENDIF
23 CLOSE(1)
100 DATA 96,26,0,0,1,26,0,0,0,0
101 DATA 0,0,0,4,0,0,0,0,0,0
102 DATA 0,0,0,0,0,0,0,0,32,111
103 DATA 0,4,44,60,0,0,1,0,220,168
104 DATA 0,12,220,168,0,20,220,168,0,28
105 DATA 63,60,0,0,63,60,0,14,78,78
106 DATA 88,143,35,192,0,0,1,26,47,60
107 DATA 0,0,0,86,63,60,0,10,63,60
108 DATA 0,13,78,78,80,143,47,60,0,0
109 DATA 0,180,63,60,0,2,63,60,0,13
110 DATA 78,78,80,143,66,103,47,6,63,60
111 DATA 0,49,78,65,72,231,32,224,97,0
112 DATA 0,162,8,40,0,1,0,32,102,0
113 DATA 0,68,8,40,0,0,0,32,103,0
114 DATA 0,10,74,40,0,31,102,0,0,38
115 DATA 17,105,0,44,0,29,52,40,0,20
116 DATA 180,104,0,22,103,0,0,20,97,0
117 DATA 0,126,36,104,0,14,19,114,32,0
118 DATA 0,46,49,66,0,20,8,169,0,2
119 DATA 0,14,76,223,7,4,78,115,8,41
120 DATA 0,2,0,0,102,236,96,180,72,231
121 DATA 32,224,97,0,0,68,8,40,0,1
122 DATA 0,32,103,0,0,46,17,105,0,44
123 DATA 0,29,8,40,0,7,0,29,103,242
124 DATA 52,40,0,20,180,104,0,22,103,0
125 DATA 0,20,97,0,0,42,36,104,0,14
126 DATA 19,114,32,0,0,46,49,66,0,20
127 DATA 8,169,0,2,0,16,76,223,7,4
128 DATA 78,115,32,121,0,0,1,26,67,249
129 DATA 0,255,250,1,78,117,82,66,180,104
130 DATA 0,18,101,0,0,4,116,0,78,117
131 DATA 0,0,0,36,6,18,196,0
9998 DATA -1
9999 DATA 14977
Adresse Zielcode Quelltext
000000 : ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
000000 : ; ;
000000 : ; PATCH-PROGRAMM ZUR BEHEBUNG DER FEHLER IN DEN HANDSHAKE-
000000 : ; PROTOKOLLEN DER SERIELLEN SCHNITTSTELLE IM ROM-TOS
000000 : ; ;
000000 : ; R. LANGE ;
000000 : ; ;
000000 : ; V15/03/87 ;
000000 : ; ;
000000 : ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
000000 : ; ;
000000 : ; DA DAS PROGRAMM RESIDENT BLEIBEN SOLL;
000000 : ; ZUERST DIE LAENGE BERECHNEN ;
000000 : ; ;
000000 :206F0004 BEGIN MOVE.L 4(SP),A0 ; BASEPAGE ADDRES TO A0
000004 :2C3C00000100 MOVE.L #$100,D6 ; BASE PAGE LENGTH TO D6
00000A :DCA8000C ADD.L $0C(A0),D6 ; ADD TEXT LENGTH
00000E :DCA80014 ADD.L $14(A0),D6 ; ADD DATA LENGTH
000012 :DCA8001C ADD.L $1C(A0),D6 ; ADD BSS LENGTH
000016 : ;
000016 : ; IN D6 IST JETZT DIE PROGRAMMLAENGE ABGELEGT
000016 : ;
000016 : ; JETZT DEN POINTER AUF DEN AUSGABEPUFFER (IOREC) HOLEN
000016 : ;
000016 :3F3C0000 MOVE.W #0,-(SP) ; DEVICE 0 = RS232
00001A :3F3C0OOE MOVE.W #14,-(SP) ; IOREC (14)
00001E :4E4E TRAP #14 ; XBIOS CALL
000020 :588F ADDQ.L #4,SP ; STACK JUSTIFY
000022 :23C00000011A MOVE.L D0,IOREC ; POINTER TO OUTBUFFER
000028 : ;
000028 : ;
000028 : ; INTERRUPT-VEKTOR DES XREADY-INTERRUPTES AUF NEUE ROUTINE LEGEN
000028 : ;
000028 :2F3C00000056 MOVE.L #XRDY_INT,-(SP) ; NEW ADDRESS OF I-ROUTINE
00002E :3F3C000A MOVE.W #10,-()SP) ; XRDY * MFP-INTERRUPT #10
000032 :3F3C0OOD MOVE.W #13,-(SP) ; MFPINT (13) XBIOS-CALL
000036 :4E4E TRAP #14
000038 :508F ADDQ.L #8.SP ; STACK JUSTIFY
00003A : ;
00003A : ; INTERRUPT-VEKTOR DES CTS-INTERRUPTES AUF NEUE ROUTINE LEGEN
00003A : ;
00003A :2F3C000000B4 MOVE.L #CTS_INT,-(SP) ; NEW ADDRESS OF I-ROUTINE
000040 :3F3C0002 MOVE.W #2,-(SP) ; CTS = MFP-INTERRUPT #2
000044 :3F3C000D MOVE.W #13,-(SP) ; MFPINT (13) XBIOS-CALL
000048 :4E4E TRAP #14
00004A :508F ADDQ.L #8,SP ; STACK JUSTIFY
00004C : ;
0000D4 :34280014 MOVE.W $14(A0),D2 ; HEAD INDEX
0000D8 :B4680016 CMP.W $16(A0),D2 ; COMPARF. WITH TAIL INDEX
0000DC :67000014 BEQ CTS2 ; TRANSMIT BUFFER EMPTY
0000E0 :6100002A BSR WRAP ; CHECK IF WRAP ARROUND
0000E4 :2468000E MOVE.L $E(A0),A2 ; SET POINTER TO XMIT-BUFFER
0000E8 :13722000002E MOVE.B $0(A2,D2.W),$2E(A1) ; BYTE TO MFP XMIT-REGISTER
0000EE :31420014 MOVE.W D2,$14(A0) ; NEW HEAD INDEX TO IOREC
0000F2 :08A900020010 CTS2 BCLR #2,$10(A1) ; CLEAR INTERRUPT SERVICE BIT
0000F8 :4CDF0704 MOVEM.L (SP)+,D2/A0-A2 ; RESTORE REGISTER
0000FC :4E73 RTE ; EXIT INTERRUPT SERVICE
0000FE : ;
0000FE : ;
0000FE : ; GETPTR LIEFERT RS232 IOREC ADRESSE ($D8E IM ROM-BIOS) IN A0
0000FE : ; UND MFP- ADRESSE IN Al
0000FE : ;
0000FE :20790000011A GETPTR MOVE.L IOREC,A0
000104 :43F900FFFA01 LEA $FFFA01,A1
00010A :4E75 RTS
00010C :
00010C : ; WRAP TESTET AUF WRAP AROUND
00010C : ;
00010C :5242 WRAP ADDQ.W #1,D2 ; TAIL INDEX +1
00010E :B4680012 CMP.W $12(A0),D2 ; = MAX BUFFERSIZE ?
000112 :65000004 BCS WRAP1 ; NO
000116 :7400 MOVEQ.L #0,D2 ; YES SET TO BEGIN
000118 :4E75 WRAP1 RTS
00011A : ;
00011A : ;
00011A : ; DAS WAR's !
00011A : ;
00011A : ;
00011A : ;
00011A : BSS
00011A : 4 IOREC DS.L 1
00011A : DATA
00011A : END
Variablentabelle
BEGIN = 00000000
CTS1 = 000000C6
CTS2 - 000000F2
CTS_INT = 00000064
GETPTR = 000000FE
IOREC = 0000011A
RTS_CTS = 000000AA
WRAP = 0000010C
WRAP1 = 00000118
XRDYO - 00000068
00004C : ; NUN DAS PROGRAMM MIT GEMDOS KEEP AUFRUF RESIDENT HALTEN
00004C : ;
00004C :4267 CLR -(SP)
00004E :2F06 MOVE.L D6,-(SP) ; PROGRAM LENGTH
000050 :3F3C0031 MOVE #$31,-(SP) ; GEM KEEP-CALL ($31)
000054 :4E41 TRAP #1 ; RETURN TO DESKTOP
000056 : ;
000056 : ;
000056 : ; NEUE XRDY-INTERRUPTROUTINE
000056 : ;
000056 :48E720E0 XRDY_INT MOVEM.L D2/A0-A2,-(A7) ; SAVE REGISTER
00005A :610000A2 BSR GETPTR ; GET POINTER FROM IOREC
00005E :082800010020 BTST #1,$20(A0) ; RTS/CTS MODE ?
000064 :66000044 BNE RTS_CTS ; YES
000068 :082800000020 XRDY0 BTST #0,$20(A0) ; XON/XOFF MODE ?
00006E :6700000A BEQ XRDY1 ; NO
000072 ;4A28001F TST.B $1F(A0) ; XOFF ACTIV-FLAG SET ?
000076 :66000026 BNE XRDY2 ; YES
Adresse Zielcode Quelltext
00007A :1169002C001D XRDY1 MOVE.B $2C(A1).$1D(A0) ; XMIT-STATUS TO IOREC
000080 :34280014 MOVE.W $14(A0),D2 ; HEAD INDEX
000084 :B4680016 CMP.W $16(A0),D2 ; COMPARE WITH TAIL INDEX
000088 :67000014 BEQ XRDY2 : TRANSMIT-BUFFER EMPTY
00008C :6100007E BSR WRAP ; CHECK IF WRAP ARROUND
000090 :2468000E MOVE.L $E(A0),A2 ; SET POINTER TO XMIT-BUFFER
000094 :13722000002E MOVE.B $0(A2,D2.W),$2E(A1) ; BYTE TO MFP XMIT-REGISTER
00009A :31420014 MOVE.W D2.$14(A0) ; NEW HEAD INDEX TO IOREC
00009E :08A90002000E XRDY2 BCLR #2,$E(A1) ; CLEAR INTERRUPT SERVICE BIT
0000A4 :4CDF0704 MOVEM.L (A7)+,D2/A0-A2 ; RESTORE REGISTER
0000A8 :4E73 RTE ; EXIT INTERRUPT SERVICE
0000AA : ;
0000AA : ; HIER IST DIE EIGENTLICHE KORREKTUR
0000AA : ;
0000AA :082900020000 RTS_CTS BTST #2,0(A1) ; CHECK IF CTS ACTIV
0000B0 :66EC BNE XP.DY2 ; NO, WAIT FOR CTS-INTERRUPT
0000B2 :60B4 BP.A XRDYO ; YES, OK SEND BYTE
0000B4 : ;
0000B4 : ;
0000B4 : ; NEUE CTS-INTERRUPTROUTINE
0000B4 : ;
0000B4 :48E720E0 CTS_INT MOVEM.L D2/A0-A2,-(SP) ; SAVE REGISTER
0000B8 :61000044 BSR GETPTR ; GET POINTER FROM IOREC
0000BC :082800010020 BTST #1,$20(A0) ; RTS/CTS MODE ?
0000C2 :6700002E BEQ CTS2 ; NO, IGNORE INTERRUPT
0000C6 :1169002C001D CTS1 MOVE.B $2C(A1),$1D(A0) ; XMIT-STATUS TO IOREC
0000CC :08280007001D BTST #7,$1D(A0) ; XMIT-REGISTER EMPTY ?
0000D2 :67F2 BEQ CTS1 ; NO. WAIT
XRDY1 = 0000007A
XRDY2 = 0000009E
XRDY_INT = 00000056