C 64
Anwendung

Kopplung über den User-Port

In der letzten Ausgabe brachten wir zwei VC 20 dazu, gegeneinander zu spielen. Nun lassen wir zwei C 64, oder einen VC 20 und einen C 64, Daten ohne jegliches Interface über den User-Port austauschen.

Seit einigen Monaten betreibe ich zwei gekoppelte C 64, die sich zu meiner vollen Zufriedenheit die Befehle eines fünfstimmigen Synthesizer-Programms teilen. Während es für meine speziellen Anforderungen bereits genügt, ein einzelnes Byte rasch zu transportieren, können die vorhandenen Routinen ohne viel Mühe so erweitert werden, daß man
a) Basicprogramme
b) Variablen,
c) Arrays und
d) Maschinenprogramme überträgt und gegebenenfalls ausführt.

In diesem Artikel erläutere ich Ihnen die Grundroutinen anhand der Basicprogrammübertragung.

Neben der eingebauten seriellen Schnittstelle hat der C 64 wie alle Computer von Commodore einen programmierbaren User-Port, der eine Verbindung nach »draußen« darstellt und entsprechend genutzt werden kann. Die Adresse des User-Ports liegt beim C 64 auf 56577 ($dd01). Eine Abfrage mit PEEK (56577) zeigt den Wert 255. Ein POKE-Befehl ändert daran nichts, weil das Betriebssystem beim Initialisieren den User-Port als Eingang definiert und sich unbeschaltete Eingänge als logisch »1« verhalten. Bevor man den User-Port als Ausgang definiert, müssen einige Sicherheitsvorkehrungen getroffen werden, denn der User-Port des einen Computers wird Draht für Draht mit dem des anderen verbunden. Dabei darf immer nur ein Computer den User-Port als Ausgang betreiben, sonst kommt es zur Zerstörung der Port-Bausteine. Die Adresse des Richtungsregisters ist 56579 ($dd03). In diesem Register definiert ein gesetztes Bit das entsprechende User-Bit als Ausgang. Dadurch wird ein gemischter Betrieb möglich, der in meinem Programm jedoch nicht vorkommt.

Als Voraussetzung muß eine physikalische Verbindung in Form von Drähten und zwei User-Port-Steckern geschaffen werden.

Es sind insgesamt 11 Drähte, welche die beiden Computer verbinden. Ich habe die Verbindung mit einer 4 m langen, sorgfältig verlöteten Flachleitung realisiert und bisher keine Störung bemerkt.

Auf Seite 143 des C 64 Handbuchs ist die Kontaktbelegung des User-Port-Steckers aufgeführt.

Die Daten-Bit-Kontakte PB0 bis PB7 liegen genau in der Mitte des Steckers und werden »Bit für Bit« verbunden, also »C« an »C«, »D« an »D« und so weiter.

Auch die Masse-Leitungen »A« werden miteinander verbunden.

Die beiden übrigen Kontakte »B« und »M« werden gekreuzt, so daß »B« mit »M« verbunden ist und umgekehrt.

Das Programm ist ausführlich erklärt. Eine Besonderheit ist die Speicherbereichswahl. Jeder Autor glaubt, der $c-Block sei ganz frei und warte auf genau sein Programm.

Um hier eine bequeme Übernahmemöglichkeit in eigene Routinen zu bieten, lädt sich der Maschinenteil des Programms an das obere verfügbare RAM-Ende, schützt das Segment vor den Strings und löscht sich letztlich selbst. Die SYS-Adressen werden auf dem Bildschirm angezeigt. Dann können im Direkt-Modus Basicprogramme hin- und hergeschoben werden, oder aus Maschinenroutinen einzelne Bytes transportiert werden. Die Basicprogramm-Übertragung hat die angenehme Eigenschaft, das transportierte Programm an das bereits im Speicher befindliche Programm anzuhängen, es handelt sich also um eine Art Merge-Funktion. Der »NEW«-Befehl schafft wieder leere Verhältnisse, wenn’s mal zu voll geworden ist.

Programmerklärung

Die Punkte »..« im Basicprogramm sind Platzhalter für die $-Seite, in die das Maschinenprogramm geladen wurde. Im folgenden gehe ich von Seite $ 7f aus. $7f00 = sys 32512 überträgt ein Basicprogramm vom Sender zum Empfänger, wo es an das im Speicher befindliche Programm angehängt wird. Zunächst wird der User-Port des Senders als Ausgang definiert. Dann wird ein Zeiger in $58 eingerichtet, der von Basic-Start ($2b) bis Basic-Ende ($2d) alle Bytes der Byte-Sende-Routine mit »jsr 7f5e« übergibt. Nach Übertragung des letzten Bytes wird der User-Port wieder als Eingang geschaltet (= Sicherheitsfall).

Mit sys 32551 ($7f27) wird ein Basicprogramm empfangen. Durch Subtraktion von 2 vom Basic-Zeiger wird die Koppeladresse gewonnen, ab der die empfangenen Bytes gespeichert werden sollen. Die Byte-Empfangs-Routine steht ab $7f7c. Der Empfänger erkennt das Basicprogramm-Ende an drei folgenden Nullen ($#00), wonach er den Empfang abbricht, durch einen CLR-Befehl ($a660) die Basic-Zeiger neu setzt, durch $a533 die Programmzeilen neu bindet und schließlich in die Basicwarteschleife nach $e385 springt.

$7f5e ist die Byte-Sende-Routine. Der Akkumulator des Prozessors wird in das Datenregister des User-Ports geschrieben, anschließend wird das dritte Bit in Register $dd00 auf »0« gesetzt. Nun bleibt das fünfte Bit in Register $dd0d des anderen Computers solange »1«, bis es gelesen wird. Durch den Lesevorgang wird es gleichzeitig gelöscht. Diesen Mechanismus kann man einfach zur Übertragungskontrolle nutzen. Das dritte Bit von $dd00 ist die VALID-Flagge. Wenn sie gesetzt ist, sind die Daten gültig. Damit der Sender weiß, wann er das Byte übertragen ansehen kann, sendet der Empfänger ein QUITTUNGs-Signal. Dieses kommt im fünften Bit des Registers $dd0d beim Sender an. Auch hier wird durch das Lesen gleichzeitig gelöscht. Braucht der Empfänger zu lange, um das Byte anzunehmen, zum Beispiel dann, wenn er gar nicht eingeschaltet ist, bricht der Sender nach zirka 1,8 Millisekunden Wartezeit den Sendevorgang ab und schaltet den USER-Port wieder auf Eingangs-Modus. Dadurch soll verhindert werden, daß die beiden Computer gleichzeitig senden, was der Hardware schaden würde. Nach erfolgter Übertragung wird das dritte Bit in $dd00 wieder auf »1« gesetzt, dabei ändert sich in $dd0d des anderen Computers nichts, es ist halt notwendig, um es wieder auf »0« setzen zu können.

$7f7c ist die Byte-Empfangs-Routine. Hier wartet der Empfänger solange in der VALID-Schleife, bis die Daten gültig sind. Wird nichts gesendet, so wartet der Empfänger fortwährend und merkt nichts. Zum Abbruch muß man dann die RUN/STOP- und die RESTORE-Taste gleichzeitig drücken. Sind die Daten gültig, so werden sie in den Akkumulator des Prozessors geladen. Der Empfänger quittiert durch einmaliges Nullen des dritten Bits von $dd00.

$7f8f definiert den User-Port als Eingang, $7f92 definiert den User-Port als Ausgang. Der Trick mit »BIT mmnn« fand auch hier Anwendung. Man spart immerhin ein Byte.

$7f9d initialisiert den User-Port. Zwar setzt das Betriebssystem den User-Port als Eingang, nimmt aber nicht die Flag in $dd0d weg, wodurch es direkt nach dem Einschalten zu einer VALID-Meldung kommt, die eine richtige Übertragung verhindert. Die VALID-Schleife in $7f9d liest so lange VALID-Flags, bis keine mehr kommen, und springt dann zur Eingangs-Richtungs-Routine.

Praktische Ausführung

Soll zum Beispiel von Computer A ein Programm zu Computer B übertragen werden, so muß zuerst Computer B mit »sys 32551« vorbereitet werden, dann wird Computer A mit »sys 32512« zum Senden veranlaßt. Im umgekehrten Fall beschwert sich Computer A mit der Meldung »device not present error«, welche aus dem Basicinterpreter für diesen Zweck entliehen wurde. Die Übertragungsrate liegt bei etwa 11 000 Bytes pro Sekunde.

(Johannes Mockenhaupt/rg)
10 rem " Johannes Mockenhaupt
20 rem " Hochstadenstr. 28
30 rem "
40 rem " D-5000 K\ln 1                                        ,den 29.M[rz 1984
50 :
60 rem " C 64 / VC 20 / Koppelung
70 rem " ]ber den programmierbaren USER Port.
80 :
100 bytes=165:                 rem " Anzahl Bytes Maschinen-PRG
110 hs=peek(56)-1:             rem " hoechste aktuelle RAM-Seite minus 1.
120 if peek(55)>0 then hs=hs-1:rem " kein 'glattes' RAM-Ende: Sicherheits-Seite
130 :
140 print "{down}sys"hs*256":BASIC PRG senden.
150 print "{down}sys"hs*256+39":BASIC PRG empfangen.
160 print "{down}sys"hs*256+94":1 BYTE senden
170 print "{down}sys"hs*256+124":1 BYTE empfangen
180 print "{down}sys"hs*256+143":USER-Port auf Eingang
190 print "{down}sys"hs*256+146":USER-Port auf Ausgang
200 print "{down}sys"hs*256+157":QUICK-Port Initialisierung
290 :
300  for i=hs*256 to i+bytes-1
310   read a
320   b=a
330    if a<0 then a=a+hs+1
340   poke i,a
350   s=s+b
360  next i
370 s=s-20510
380 if s<>0 then print "{down}{rvon}Data-Fehler{rvof}:"s"betraegt die Differenz.":end
390 :
400 print "{down}{rvon}QuickPort{rvof} ist jetzt initialisiert.
410 poke 56,hs:    rem " neue RAM-top Adresse
420 sys hs*256+157:rem " Initialisierung
430 new:           rem " Setzen der BASIC-Zeiger auf neuen Bereich.
440 :
6000 rem " Transport-Routinen     BASIC-PRG  Senden:    $..00
6010 :
6100 data  32,146, -1:rem " ..00  20 92 ..  jsr ..92    USER-Port auf Ausgang
6110 data 165, 43:    rem " ..03  a5 2b     lda 2b      L-Byte Basic-Start
6120 data 164, 44:    rem " ..05  a4 2c     ldy 2c      H-Byte Basic-Start
6130 data 133, 88:    rem " ..07  85 58     sta 58             merken
6140 data 132, 89:    rem " ..09  84 59     sty 59             merken
6200 data 160,  0:    rem " ..0b  a0 00     ldy#00      Y-Register vorbereiten.
6210 data 177, 88:    rem " ..0d  b1 58     lda (58).y  1 Byte aus PRG holen
6220 data  32, 94, -1:rem " ..0f  20 5e ..  jsr .. 5e   und senden.
6230 data 230, 88:    rem " ..12  e6 58     inc 58      Zeiger erhoehen L-Byte
6240 data 208,  2:    rem " ..14  d0 02     bne ..18    ohne Uebertrag
6250 data 230, 89:    rem " ..16  e6 59     inc 59      Zeiger erhoehen H-Byte
6300 data 165, 89:    rem " ..18  a5 59     lda 59      Zeiger     H-Byte
6310 data 197, 46:    rem " ..1a  c5 2e     cmp 2e      BASIC-Ende H-Byte
6320 data 144,237:    rem " ..1c  90 ed     bcc ..0b    noch nicht erreicht.
6330 data 165, 88:    rem " ..1e  a5 58     lda 58      Zeiger     L-Byte
6340 data 197, 45:    rem " ..20  c5 2d     cmp 2d      BASIC-Ende L-Byte
6350 data 144,231:    rem " ..22  90 e7     bcc ..0b    noch nicht erreicht.
6360 data  76,143, -1:rem " ..24  4c 8f ..  jmp ..8f    USER-Port auf Eingang
6370 :
7000 rem " Transport-Routinen     BASIC-PRG  Empfangen: $..27
7010 :
7100 data  56:        rem " ..27  38        sec         Start-Adresse berechnen
7110 data 165, 45:    rem " ..28  a5 2d     lda 2d       L-Byte Basic-Ende
7120 data 233,  2:    rem " ..2a  e9 02     sbc#02       $02 substrahieren
7130 data 133, 45:    rem " ..2c  85 2d     sta 2d       und speichern.
7140 data 165, 46:    rem " ..2e  a5 2e     lda 2e       H-Byte Basic-Ende
7150 data 233,  0:    rem " ..30  e9 00     sbc#00       Carry-Flag subtrahieren
7160 data 133, 46:    rem " ..32  85 2e     sta 2e       und speichern.
7200 data 160,  0:    rem " ..34  a0 00     ldy#00      Y-Register vorbereiten.
7210 data  32,124, -1:rem " ..36  20 7c ..  jsr ..7c    1 Byte empfangen
7220 data 145, 45:    rem " ..39  91 2d     sta (2d).y  und speichern.
7230 data 230, 45:    rem " ..3b  e6 2d     inc 2d      BASIC-PRG Ende erhoehen
7240 data 208,  2:    rem " ..3d  d0 02     bne ..41    ohne Uebertrag
7250 data 230, 46:    rem " ..3f  e6 2e     inc 2e      H-Byte erhoehen
7300 data 168:        rem " ..41  a8        tay         Ende-Bedingung pruefen
7310 data 208,  8:    rem " ..42  d0 08     bne ..4c    kein Ende
7320 data 165,252:    rem " ..44  a5 fc     lda fc      2.Byte=0 ?
7330 data 208,  4:    rem " ..46  d0 04     bne ..4c    nein, kein Ende
7340 data 165,253:    rem " ..48  a5 fd     lda fd      3.Byte=0 ?
7350 data 240,  9:    rem " ..4a  f0 09     beq ..55    ja, dann Ende
7360 data 165,252:    rem " ..4c  a5 fc     lda fc      vorletztes Byte kellern
7370 data 133,253:    rem " ..4e  85 fd     sta fd
7380 data 132,252:    rem " ..50  84 fc     sty fc      letztes    Byte kellern
7390 data  24:        rem " ..52  18        clc         Sprungbedingung
7400 data 144,223:    rem " ..53  90 d9     bcc ..34    weiter machen.
7500 data  32, 96,166:rem " ..55  20 60 a6  jsr a660    CLR-Befehl
7510 data  32, 51,165:rem " ..58  20 33 a5  jsr a533    BASIC-Zeilen binden
7520 data  76,133,227:rem " ..5b  4c 85 e3  jmp e385    in READY-Modus springen
7530 :
8000 rem " Transport-Routinen: 1 BYTE Senden:    $..5e
8010 :
8100 data 141,  1,221:rem " ..5e  8d 01 dd  sta dd01    AKKU in USER-Port
8110 data 162,147:    rem " ..61  a2 93     ldx#93      VALID-Signal senden
8115 data 142,  0,221:rem " ..63  8e 00 dd  stx dd00
8120 data 173, 13,221:rem " ..66  ad 0d dd  lda dd0d    QUITTUNG laden
8125 data 208, 11:    rem " ..69  d0 0b     bne ..76    QUITTUNG empfangen
8130 data 202:        rem " ..6b  ca        dex         abzaehlen der Zeit
8135 data 208,248:    rem " ..6c  d0 f8     bne ..66    Ueberschreitung?
8140 data  32,143, -1:rem " ..6e  20 8f ..  jsr ..8f    USER-Port als Eingang
8150 data 162,  5:    rem " ..71  a2 05     ldx#05      Fehler-Nummer Bet.System
8155 data  76, 58,164:rem " ..73  4c 3a a4  jmp a43a    Fehler-Routine Rechner
8160 data 162,151:    rem " ..76  a2 97     ldx#97      VALID-Flag aus
8165 data 142,  0,221:rem " ..78  8e 00 dd  stx dd00    VALID zuruecknehmen
8170 data  96:        rem " ..7b  60        rts         RETURN
8180 :
8200 rem " Transport-Routinen: 1 BYTE Empfangen: $..7c
8202 :
8204 data 173, 13,221:rem " ..7c  ad 0d dd  lda dd0d    VALID holen
8210 data 240,251:    rem " ..7f  f0 fb     beq ..7c      ''  warten
8220 data 173,  1,221:rem " ..81  ad 01 dd  lda dd01    BYTE  holen
8230 data 162,147:    rem " ..84  a2 93     ldx#93      QUITTUNG-Signal senden
8235 data 142,  0,221:rem " ..86  8e 00 dd  stx dd00
8240 data 162,151:    rem " ..89  a2 97     ldx#97      QUITTUNG-Signal loeschen
8245 data 142,  0,221:rem " ..8b  8e 00 dd  stx dd00
8250 data  96:        rem " ..8e  60        rts         RETURN
8260 :
9000 rem " Richtungs-Register auf Eingang:  $..8f
9010 rem "                        Ausgang:  $..92
9020 :
9100 data 162,  0:    rem " ..8f  a2 00     ldx#00      USER-Port als Eingang
9110 data  44,162,255:rem " ..92  a2 ff     ldx#ff      USER-Port als Ausgang
9120 data 160,151:    rem " ..94  a0 97     ldy#97
9130 data 140,  0,221:rem " ..96  8c 00 dd  sty dd00    VALID-Flag zuruecksetzen
9140 data 142,  3,221:rem " ..99  8e 03 dd  stx dd03    USER-Richtung setzen
9150 data  96:        rem " ..9c  60        rts         RETURN{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}
9160 :
9200 rem " Initialisierung des Ports        $..9d                       {$a0}{$a0}{$a0}{$a0}{$a0}
9210 :
9220 data 173, 13,221:rem " ..9d  ad 0d dd  lda dd0d    VALID laden{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}{$a0}
9230 data 208,251:    rem " ..a0  d0 fb     bne ..9d    Flag gesetzt:noch einmal
9240 data  76,143, -1:rem " ..a2  4c 8f ..  jmp ..8f    USER-Port als Eingang
Listing zur Übertragung von Basicprogrammen zwischen zwei C 64.
Die Verbindung von User-Port zu User-Port
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →