Commodore 64/128 mit PC verbinden, Teil 4

Im abschließenden Teil dieser Serie habe ich mir vorgenommen, eine kleine Beispielanwendung in MOS 6502 Assembler zu schreiben. Der PC soll über das installierte, serielle Kabel gegenüber dem C64/C128 wie ein Client auftreten, der Zeichen an den Server (den Commodore Computer) sendet. Das Programm dient mehr als Demo und weniger als vollständig ausgetestete Software. Ich habe die einzelnen Code-Abschnitte gut dokumentiert, so dass man hoffentlich schnell eigene Experimente machen kann. Der Assembler-Code wurde im Turbo Macro Pro (TMP) erstellt, kann aber sicher leicht auf andere Assembler übertragen werden.

Das Programm liegt ausführbar in Maschinensprache ab Adresse $1000 (4096) und kann aus Basic heraus mit dem Befehl „SYS 4096“ gestartet werden:

*= $1000
; open rs-232 serial channel and read
; in bytes until x pressed

Am Anfang werden alle Einsprungadressen für Systemaufrufe definiert. Dafür werden fast durchgängig die empfohlenen, fixen Adressen der ROM Sprungtabelle („jump table“) verwendet:

; define kernel system calls
chkin = $ffc6; set input channel
chkout = $ffc9; set output channel
close = $ffc3; close logical file
clrchn = $ffcc; set default i/o dev
chrin = $ffcf; get chr from input dev
chrout = $ffd2; print accu to output
getin = $ffe4; get chr from input dev
linprt = $bdcd; print number
open = $ffc0; open logical device
readst = $ffb7; read i/o status
setnam = $ffbd; set filename params
setlfs = $ffba; set log file nr…
strout = $ab1e; print string

Jetzt folgt ein Schalter, ob man gewisse Debug-Informationen ausgeben lassen möchte. Steht der Schalter auf „1“ und nicht auf „0“, dann wird mit jedem Drücken der „D“-Taste ein kurzer Textblock ausgegeben, der später erklärt wird.

debug    = 1

Anschließend wird der Zeichensatz fest auf „lower/uppercase“ eingestellt, Bildschirmfarbe und -rahmen festgelegt, der Bildschirm gelöscht sowie eine Willkommensbotschaft am Bildschirm ausgegeben.

; lower/uppercase chr set
         lda $d018
         ora #%00000010
         sta $d018

; set up screen
         lda #6    ; blue
         sta $d021 ; background color
         sta $d020 ; border color
         lda #147 ; clear screen
         jsr chrout
         lda #<welcome
         ldy #>welcome
         jsr strout

Nun wird die RS-232 Schnittstelle parametrisiert und anschließend geöffnet.

; 6551 control register
; set 8n1, 300 baud
         lda #%00000110
         sta $0293

; 6551 command register
; set no parity, half-duplex, 3 line
         lda #%00010000
         sta $0294

; set logical file number
         lda #$02 ; log file nr
         ldx #$02 ; dev nr
         ldy #$00 ; sec address
         jsr setlfs

; set filename parameters
         lda #$00 ; no filename
         jsr setnam

; open rs-232 channel
         jsr open

Hier folgt jetzt die Hauptschleife. In dieser wird ein Byte von der RS-232 Schnittstelle gelesen (besser gesagt vom Puffer). Danach wird exemplarisch Bit 2 (%0100 = 4) des Status-Registers auf einen Receiver Buffer Overrun getestet. Sollte dieser vorliegen, wird in eine Fehlerroutine verzweigt. Anschließend wird ein Zeichen aus dem Tastaturpuffer in den Akkumulator eingelesen. Wurde vom Anwender die „X“-Taste gedrückt, wird das Programm beendet. Hat der Anwender die „D“-Taste gedrückt, wird am Bildschirm des C64/C128 eine Debug-Meldung ausgegeben.

loop
; get byte from rs-232 channel
         ldx #$02 ; rs-232
         jsr chkin
         jsr getin
         jsr chrout
         jsr clrchn

; test status reg for buffer overrun 
         lda $0297
         and #4
         bne error

; get a character from keyboard
         jsr getin
         cmp #68 ; d key pressed?
         beq sdebug
         cmp #88 ; x key pressed?
         bne loop
sclose
; close rs-232 channel
         lda #$02
         jsr close
         rts ; return to basic

Die sehr rudimentäre Fehlerbehandlung folgt:

error
         lda #<errormsg
         ldy #>errormsg
         jsr strout
         jmp sclose ; end program

Gefolgt vom Code für die Debug-Meldung:

sdebug
         lda #13
         jsr chrout
         lda #<d1
         ldy #>d1
         jsr strout
         lda $0297 ; 6551 status reg
         ora #48
         jsr chrout
         lda #13
         jsr chrout
         lda #<d2
         ldy #>d2
         jsr strout
         ldx $029c ; start rec buffer
         lda #$00
         jsr linprt
         lda #13
         jsr chrout
         lda #<d3
         ldy #>d3
         jsr strout
         ldx $029b ; end receive buffer
         lda #$00
         jsr linprt
         lda #13
         jsr chrout
         lda #<d4
         ldy #>d4
         jsr strout
         ldx $38 ; end of user ram
         lda #$00
         jsr linprt
         lda #13
         jsr chrout
         jmp loop

d1       .text "6551 status register "
         .null "at address $297: "
d2       .text "rs-232 start "
         .null "receive buffer $29c: "
d3       .text "rs-232 end "
         .null "receive buffer $29b: "
d4       .text "pointer to end of "
         .null "basic ram: "

Abschließend die noch fehlenden Zeichenketten für die Bildschirmausgabe:

errormsg .null "error: buffer overrun!"

welcome  .text "terminal ready"
         .byte 13
         .text "waiting for input from "
         .null "serial line: "
         .end

Startet man nun auf dem C64/C128 das Programm und hat am PC ein verbundenes Terminal-Programm am Laufen (ich verwende PuTTY), dann erscheinen die am PC eingetippten Zeichen

am Bildschirm des C64/C128. Allerdings wird erst die Willkommensbotschaft „terminal ready, waiting for input from serial line“ ausgegeben:

Nach jedem vom PC gesendeten „Return“ habe ich die „D“-Taste gedrückt und somit die Debug-Meldung erhalten. Nach der dritten Eingabe hatte ich das Programm mit „X“ beendet und damit wieder die Eingabeaufforderung „ready.“ von Basic bekommen.

Die Debug-Meldung ist nicht schön, sagt aber ein bisschen etwas aus über den Zustand unserer seriellen Verbindung zum PC. Die erste Zeile mit dem Wert „8“ ist der Wert der Adresse $297 (RSSTAT). Das gesetzte Bit 3 ergibt den Wert %1000 (8) und bedeutet „Receive Buffer Empty“. Klingt erst einmal komisch, macht aber Sinn, wenn man weiß, dass die gesendeten Zeichen (wie „hello from your pc“ bereits mittels GETIN-Befehl aus dem Puffer gelesen wurden. Dass der Pufferzeiger immer weiter verschoben wird, sieht man an den Adressen $29c und $29b. Nach dem „hello from your pc“ steht sowohl der Anfangs- als auch der Endzeiger auf dem Wert „190“. Nach dem Abholen des Zeichens „1“ (gefolgt von einer Null-Terminierung) werden beide Zeiger folglich um zwei Stellen weiter verschoben auf „192“ („1“ plus $00). Das gleiche passiert dann nach dem Abholen des Zeichens „2“. Jetzt stehen beide Zeiger auf dem Wert „194“.

Der Zeiger an Adresse $38 (MEMSIZ) auf das Ende des RAM-Bereichs steht auf 121. Damit ist die letzte verfügbare Adresse im RAM 121 * 256 -1 = 30975 ($78FF). Dazu muss man folgendes verstehen: Startet man einen C64 oder einen C128 im C64-Modus und erhält die Eingabeaufforderung von Basic, so steht die Adresse $38 noch auf dem Wert „160“: 160 * 256 -1 = 40959 ($9FFF). Lädt man nun den Assembler TMP v1.2 in den Hauptspeicher (RAM), dann findet sich in Adresse $38 der reduzierte Wert „121“: 121 * 256 – 1 = 30975 ($78FF), TMP hat also Speicherplatz im RAM ab Adresse $78FF reserviert, um den Assembler-Code des Anwenders unterzubringen. An Adresse $8000 befindet sich dann die Einsprungadresse für TMP selbst.

Es wäre jetzt relativ leicht den Code weiterzuentwickeln und eine echte Client/Server-Applikation zu bauen: der PC sendet ein Kommando wie „clear“ per Terminalprogramm und serieller Leitung an den C64/C128. Dieser liest „clear“ aus dem Empfangs-Puffer aus, interpretiert dies als einen Befehl an sich als Server und führt den folgenden, einfachen Code zum Löschen des Bildschirms aus:

         lda #147 ; clear screen
         jsr chrout

Hat man diesen Schritt gemeistert, ist es nicht mehr weit zum eigenen Bulletin Board-System 😉

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert


Der Zeitraum für die reCAPTCHA-Überprüfung ist abgelaufen. Bitte laden Sie die Seite neu.