ST-Tips: ATARI-BIOS-Patch zur Behebung von Fehlern der seriellen Schnittstelle

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


Aus: ST-Computer 08 / 1987, Seite 112

Links

Copyright-Bestimmungen: siehe Über diese Seite