Tips & Tricks für Profis
Bisher erschien es nicht möglich, auf dem C 64 Daten mit 4800 bit/s zu übertragen. Ein kleines Programm schafft Abhilfe. Außerdem läßt sich der eigentlich nicht nutzbare Bildschirmrahmen nun doch sinnvoll beschreiben.
Im Handbuch des C 64 steht überhaupt nichts, im Programmierhandbuch nichts Genaues — was ist mit den freidefinierbaren Übertragungsraten los?
Beim C 64 handelt es sich bei der RS232-Schnittstelle um eine bitserielle, asynchrone Übertragung. Das heißt ein Byte wird in Bits zerteilt und diese werden dann über eine Leitung in Form von Spannungswechseln gesendet. Unter asynchroner Übermittlung versteht man die Verwendung von Kennzeichen für Byteanfang und Byteende. Bei manchen Computern können die Daten wunschweise auch synchron übermittelt werden. Der Computer sendet dann kein Start- oder Stoppbit mehr, es werden nur noch die Daten übertragen. Vorteil: Die Übertragungszeit verkürzt sich, da die Steuerbits entfallen.
RS232 mit 4800 bit/s
Beim C 64 ist die Übertragung rein softwaremäßig geregelt. Er setzt dazu den CIA #2 ein. Dieser Baustein besitzt zwei 16-Bit-Intervall-Timer, die von einem bestimmten Wert auf Null zählen und dann einen NMI auslösen können. Dies nutzt die RS232-Schnittstelle aus. Die Software holt den Wert für den Timer B, der als Interruptgenerator dient, aus einer im ROM verankerten Tabelle. Es existieren hierzu zwei verschiedene Tabellen, aber dazu später mehr. Diese Werte werden in den Timer und in zwei Speicherstellen gebracht. Danach startet der Timer. Da beim C 64 das Senden und Empfangen über RS232 interruptgesteuert geschieht, werden zwei Puffer (Zwischenspeicher) benötigt. Einer der beiden Puffer steht für die zu sendenden Daten bereit. Die Daten werden nur in den Puffer geschrieben. Das Senden geschieht mit Hilfe des NMI-Interrupts, weil man ihn (im Gegensatz zum IRQ) nicht sperren kann. So werden die Daten in jedem Fall gesendet, es sei denn, man stoppt den Timer oder verbiegt den NMI-Vektor. Da die Daten in den Puffer geschrieben werden und das Beschreiben des Puffers normalerweise schneller geht als das Senden der Zeichen, sollte man, nachdem ein Zeichen gesendet wurde, das nächste Zeichen vorbereiten und warten, bis der Puffer frei ist. Nun kann das Zeichen problemlos in den Puffer geschrieben werden. Beachtet man dies nicht, könnte versehentlich ein noch nicht gesendetes Zeichen überschrieben werden. Die Folge wäre eine fehlerhafte Übertragung. Das Empfangen geschieht auf ähnliche Weise, denn die RDX-Leitung (hier gelangen die Daten vom anderen Computer zum C 64) ist nicht nur mit dem Port B des CIA #2 verbunden, sondern auch mit dem Eingang FLAG der CIA, der einen NMI auslösen kann. Das Empfangen geschieht also unabhängig vom Programm, daher werden die Daten in den Empfangspuffer geschrieben, der genauso groß ist wie der Sendepuffer: 256 Byte. Man sollte sich also darum kümmern, daß die Zeichen rechtzeitig aus dem Puffer gelesen werden. Durch die Kernelroutine READST, oder beim C 64 auch durch Auslesen der Speicherstelle $90, läßt sich der Status abfragen. Bei gesetztem Bit 2 lief der Empfangspuffer über. Um nun andere Übertragungsraten zu benutzen, könnten natürlich die Werte in den Timer und die entsprechenden Werte in die beiden Speicherstellen geschrieben werden. Dies hätte nur den großen Nachteil, daß die so erstellten Programme schwer an andere (Commodore-)Computer anzupassen wären, da diese nicht die gleichen Speicherstellen benutzen. Aber dies ist gar nicht nötig, denn es geht auch sehr viel eleganter! Die RS232-Schnittstelle öffneten wir bisher folgendermaßen:
lda #2 ;Logische Filenummer = 2
ldx #2 ;Gerätenummer der RS232
ldy #3 ;Sekundäradresse (ohne Funktion)
jsr setpar ;Werte setzen
lda #2 ;Länge des Filenamens
ldx #<fadr ;Adresse des Filenamens (low)
ldy #>fadr ;(high)
jsr setnam ;Name für OPEN setzen
jsr open ;Datei öffnen
ldx #<ebuf ;Zeiger für Empfangspuffer
ldy #>ebuf ;und
stx $f7
sty $f8
ldx #<abuf ;Ausgabepuffer setzen
ldy #>abuf
stx $f9
sty $fa
fadr .byte %00000110 ;Wert für Steuerregister
.byte %00000000 ;Wert für Befehlsregister
Übrigens werden die Zeiger auf die Puffer auch vom Betriebssystem gesetzt, das die Puffer an das obere (Basic-) RAM-Ende legt. Dies ist auch der Grund, weswegen beim Öffnen der Schnittstelle ein CLR ausgeführt wird, denn im gleichen Speicherbereich liegen ja auch die Stringvariablen. Wenn wir jetzt zum Beispiel sequentielle Dateien senden oder empfangen wollten, könnte es bei langen Dateien passieren, daß die Puffer und die Datei sich überlappen, so daß Datenverluste entstehen. Darum legen wir die Puffer in uns angenehme Speicherbereiche, was man entweder vor oder nach dem OPEN-Befehl machen kann. Vorher deswegen, da das Betriebssystem die Pufferadressen erst festlegt, nachdem es in den Speicherstellen nachgesehen hat, ob das vom Programm schon gemacht wurde. Ist das High-Byte der Pufferzeiger ungleich Null, wird davon ausgegangen, daß die Zeiger bereits auf die Puffer deuten. Deshalb wird nichts mehr verändert. Und nun geht's los!
Übersicht der Werte für:
| Steuerregister | Befehlsregister |
|---|---|
| Bit 7: 0 1 Stoppbit 1 2 Stoppbits |
Bit 7,6,5: 000 keine Parität 001 gerade Parität 011 ungerade Parität 101 8. Datenbit = 1 111 8. Datenbit = 0 |
| Bit 5,6: 00 8 Datenbits 01 7 Datenbits 10 6 Datenbits 11 5 Datenbits |
Bit 4: 0 Vollduplex 1 Halbduplex |
| Bit 0,1,2,3: 0000 selbstdef. Übertr. 0001 50 bit/s 0010 75 bit/s 0011 110 bit/s 0100 134,5 bit/s 0101 150 bit/s 0110 300 bit/s 0111 600 bit/s 1000 1200 bit/s 1001 1800 bit/s 1010 2400 bit/s |
Bit 0 (Handshake): 0 3-Line (Software-) 1 X-Line (Hardware-) |
Der Filename kann auch vier Zeichen lang sein. Die ersten beiden Zeichen behalten ihre Bedeutung bei, aber die nächsten beiden beinhalten den Wert für die Übertragungsrate.
Der Filename sieht also wie folgt aus:
»Steuerregister« »Befehlsregister« »Übertragungsrate-low« »Übertragungsrate-high«
Wie gesagt, das untere Nibble vom »Steuerregister« muß %0000 sein! Die Werte für »Übertragungsrate-low« und »Übertragungsrate-high« berechnet man wie folgt:
Übertragungsrate-high = INT(Prozessortakt/ Übertragungsrate/2-100)/256)
Übertragungsrate-low= INT(Prozessortakt/ Übertragungsrate/2-100-Übertragungsrate-highx256)
Der Prozessortakt beträgt bei der deutschen PAL-Version 985259 Hertz, bei der amerikanischen NTSC-Version 1022730 Hertz. Das heißt nichts anderes, als daß die Programme auf amerikanischen Geräten schneller laufen als auf deutschen. Da dies natürlich auch Auswirkungen auf die Übertragungskapazität hat, sind im Betriebssystem zwei Tabellen für den Timer B vorhanden. Wenn Sie nun Programme mit selbstdefinierten Übertragungsraten schreiben, sollten Sie immer daran denken, daß diese Programme entweder nur auf dem deutschen oder dem amerikanischen C 64 richtig laufen, wenn Sie nur eine Tabelle verwenden. Durch Auslesen der Speicherstelle $02A6 können Sie ganz einfach feststellen, ob Sie eine PAL- oder eine NTSC-Version vor sich haben. Bei einer PAL-Version steht in besagter Speicherstelle der Wert 1, bei NTSC eine Null.
Nun ein Beispiel: Die Übertragungsrate soll 1800 Bit/s betragen. Also erhalten wir für »Übertragungsrate-high«: INT((985250/4800/2-100)/256)=0 und für »Übertragungsrate-low«: INT(985250/4800/2-100-0x256)=2. Der Filename lautet also: %XXXX0000, %XXXXXXXX, %00000010, %00000000 Achtung: Für die X-Bits setzen Sie die entsprechenden Werte aus der Tabelle ein.
Übungen:
Wir möchten ein sequentielles File mit 4800 bit/s, 7 Datenbits, 1 Stoppbit, 3-Line-Handshake und keiner Parität senden. Also: Zuerst das sequentielle File in den Speicher laden. Jetzt müssen wir noch die RS232-Schnittstelle öffnen, wobei wir auf die Abfrage auf PAL- oder NTSC-Version verzichten. Das Programm zum Öffnen ist bis auf den Filenamen und dessen Länge von dem altbekannten nicht zu unterscheiden.
lda #2 ;logische Filenummer = 2
ldx #2 ;Geraetenummer der RS232
ldy #3 ;Sekundaeradresse
jsr setlfs ;Werte setzen
lda #4 ;der Filename ist 2 Zeichen lang
ldx #<fadr ;Adresse des Filenamens (low)
ldy #>fadr ;(high)
jsr setnam ;Name für OPEN setzen
jsr open ;Datei oeffnen
ldx #2 ;Datei mit der logischen Filenummer
jsr chkout ;#2 als Standardausgabegerät
Nun müssen Sie einen Zeiger auf den Anfang des in den Speicher geladenen sequentiellen Files setzen und die Pufferzeiger entsprechend ändern.
(Zeiger »pt« auf Dateianfang setzen)
sende ldy #0 ;aktuelles Zeichen aus dem
lda (pt),y ;File in den Akku holen
(Zeichen nun bearbeiten, zum Beispiel in ASCII-Code
wandeln)
tax ;Zeichen merken
wloop lda $02a1 ;ist das vorherige Zeichen
and #1 ;bereits gesendet worden?
bne wloop ;nein: =>warten
txa ;gemerktes Zeichen zurueckholen
jsr chrout ;auf RS232 ausgeben
Der Zeiger »PT« muß nun erhöht und auf Fileende getestet werden. Ist die Datei noch nicht zu Ende, muß wieder zum Label SENDE gesprungen werden.
jsr clrchn ;Standardwerte Ein-/Ausgabe
lda #2 ;setzen und Datei schließen
jsr close
rts ;das war's!
fadr .byte %00100000
.byte %00000000
.byte %00000010
.byte %00000000
Das folgende Programm (Listing 1, bitte mit dem MSE eingeben) sendet sequentielle Files mit 4800 Bit/s, 8 Datenbits, 1 Stoppbit, keine Parität, Vollduplex und 3-Line-Handshake. Die Syntax lautet: SYS 52480,konv-flag," Name" Wenn konv-flag = 0, werden die Zeichen erst in ASCII-Code gewandelt und dann gesendet. Bei konv-flag = 128 erfolgt die Übertragung unverändert.
Mit dem Namen ist der Titel der sequentiellen Datei gemeint. »,S,R"« wird vom Programm nicht automatisch ergänzt. Sie müssen es also noch an den Namen anhängen. Beispiel: SYS 52480,128," TEST,S,R"
Die Datei TEST wird unkonvertiert gesendet.
(Martin Müller/aw)PROGRAMM : SCREENCOPY $CF11 CF11 CFFF ----------------------------------- CF11 : 78 A2 23 A0 CF 8E 14 03 7F CF19 : 8C 15 03 58 A9 41 8D F2 BC CF21 : CF 60 AD 8D 02 49 07 29 16 CF29 : 07 F0 03 4C 31 EA AD 0E 30 CF31 : DC 29 FE 8D 0E DC A9 01 84 CF39 : A2 08 A0 01 20 BA FF A9 53 CF41 : 06 A2 ED A0 CF 20 BD FF 1D CF49 : 20 C0 FF A2 01 20 C9 FF 56 CF51 : A9 40 20 D2 FF A9 00 20 0A CF59 : D2 FF AD 88 02 85 F8 A9 2B CF61 : 00 85 F7 A9 19 8D EC CF A8 CF69 : A9 28 8D EB CF A0 00 20 49 CF71 : F3 CF B1 F7 49 80 91 F7 86 CF79 : 20 F9 CF C9 20 D0 04 A9 AF CF81 : DF D0 21 C9 A0 F0 1D 49 E2 CF89 : 80 29 7F C9 20 B0 05 09 65 CF91 : 40 4C A5 CF C9 40 90 0C 54 CF99 : C9 60 B0 05 09 80 4C A5 70 CFA1 : CF 18 69 40 20 D2 FF A0 B9 CFA9 : 00 20 F3 CF B1 F7 49 80 B1 CFB1 : 91 F7 20 F9 CF E6 F7 D0 3B CFB9 : 02 E6 F8 CE EB CF D0 AD 22 CFC1 : A9 28 8D EB CF A9 20 20 6A CFC9 : D2 FF CE EB CF D0 F6 CE C9 CFD1 : EC CF D0 94 20 CC FF A9 27 CFD9 : 01 20 C3 FF EE F2 CF AD FC CFE1 : 0E DC 09 01 8D 0E DC 4C 15 CFE9 : 31 EA 00 00 42 49 4C 44 B8 CFF1 : 20 41 78 A2 34 86 01 60 60 CFF9 : A2 37 86 01 58 60 A1
Änderung der Tastaturbelegung
Es kommt des öfteren vor, daß man anstelle der alten Tastaturbelegung eine andere benutzen möchte, um beispielsweise Programme leichter eingeben zu können. Oder man möchte anstelle der amerikanischen QWERTY- eine QWERTZ-Tastatur haben. Eine Lösungsmöglichkeit wäre, die Zeichen Y und Z im Zeichensatz umzudefinieren. Die gebräuchlichste Lösung dieses Problems ist, das ROM ins RAM zu kopieren und die Zeichen in der Tastaturtabelle auszutauschen. Will man dies jedoch in Basic ausführen, ist das sehr zeitaufwendig und schwierig.
Das folgende Programm (Listing 2) macht es Ihnen sehr leicht. Bis auf die Tasten <SHIFT>, <CBM>, <CTRL> und <RESTORE> kann jeder Taste des C 64 ein beliebiger CHR$-Code zugeordnet werden. Der Tasten-Code stimmt mit dem jeweiligen Bildschirm-Code überein. Die Syntax des neuen Befehls lautet: !A,B
A ist der Bildschirm-Code, dem der CHR$-Code B zugeordnet wird. Wollen wir beispielsweise das Zeichen A in ein B umwandeln, muß folgender Befehl angewandt werden: !1,66 (und !2,65, falls das B in ein A geändert werden soll).
Die Zahl 1 ist der Bildschirm-Code des Zeichens A, dem der Wert 66 (CHR$-Code von B) zugeordnet wird. Es ist dabei unwichtig, ob das B vorher geändert war oder nicht. Der CHR$-Code wird aus dem ROM gelesen.
Um eine QWERTZ-Tastatur zu erhalten, sind folgende Befehle einzugeben:
!25,90 (wandelt Y in Z um)
!26,89 (wandelt Z in Y um)
Soll die Cursor-Steuerung auf die Funktionstasten gelegt werden, so benötigt man folgende Befehle:
!39,145 (<F1> = <CRSR>-hoch)
!40,17 (<F3> = <CRSR>-unten)
!41,157 (<F5> = <CRSR>-links)
!63,29 (<F7> = <CRSR>-rechts)
Beim Abtippen von »DATA-Wüsten« lohnt es sich, die Komma-Taste zu verschieben. Der Befehl !31,44 verlegt diese auf die <←>-Taste.
Einige der Zeichen können jedoch nicht geändert werden. Es sind die Bildschirmcodes von 33 bis 41 sowie der Code 63. Diese Zeichencodes sind anderweitig belegt.
| Zeichencode | Neue Belegung |
|---|---|
| 33 | <CRSR>-links |
| 34 | <CRSR>-hoch |
| 35 | <DEL> |
| 36 | <HOME> |
| 37 | <STOP> |
| 38 | <RETURN> |
| 39 | <F1> |
| 40 | <F3> |
| 41 | <F5> |
| 63 | <F7> |
Im Klartext heißt das, daß Sie, wenn Sie den Bildschirm-Code 33 mit dem Befehl !33,65 ändern wollen, nicht das Ausrufungszeichen, sondern die <CRSR>-links-Taste in ein A umwandeln.
Die Initialisierung der Routine erfolgt durch SYS 49152. Danach steht Ihnen der Befehl !A,B zur Verfügung. Der Wertebereich von A und B liegt zwischen 0 und 255. Löschen läßt sich die Tastenbelegung, indem man die ursprüngliche Belegung aus dem ROM kopiert. Dies geschieht durch: POKE 1,55:SYS 49152
(Andreas Schindler/dm)0 data120,162,53,142,8,3,162,192,142,9,3,169,0,133,251,133,253,169,160 1 data133,252,169,224,133,254,162,32,160,0,177,251,145,251,177,253,145 2 data253,200,208,245,230,254,230,252,202,208,238,169,53,133,1,88,96,32 3 data115,0,201,33,240,6,32,121,0,76,231,167,32,115,0,32,158,183,134,2 4 data160,0,224,64,144,14,224,128,144,8,224,192,144,2,200,200,200,200,200 5 data200,152,170,189,121,235,133,251,189,122,235,133,252,165,2,41,63,170 6 data189,135,192,133,2,32,253,174,32,158,183,138,164,2,145,251,169,53 7 data133,1,76,174,167,234,46,10,28,20,18,14,21,26,29,33,34,37,42,36,39 8 data38,41,62,17,13,22,30,31,9,23,25,12,64,48,64,54,57,60,2,7,0,51,63 9 data1,4,5,6,49,40,47,43,44,55,56,59,8,11,16,19,24,27,32,35,45,50,64,53,64,3 10 fori=49152to49350:reada:pokei,a:next 11 rem aktivieren sys49152 12 rem syntax !a,b
Laufschrift im Bildschirmrahmen
Seit einiger Zeit ist es in Mode gekommen, im Titelbild mancher Programme mindestens eine Laufschrift darzustellen. Nachdem sich die Programmiertechnik hier immer weiter fortentwickelt hat, so daß sich die Laufschriften jetzt schon hochauflösend und flimmerfrei bewegen, bleibt immer noch ein großes Problem: Jede Laufschrift benötigt Platz auf dem Bildschirm. Will man zum Beispiel eine Laufschrift in ein HiRes-Bild einblenden, mußte bisher immer das Bild so weit angepaßt werden, daß eine oder zwei Zeilen für die Laufschrift freiblieben. Hier stellt sich die Frage: Muß das so sein?
Nein, denn wozu gibt es den weitgehend ungenutzten Bildschirmrand, der lange Zeit immer nur die Farbe annehmen konnte, die in $D020 angegeben war. Vor einiger Zeit kam jemand auf die Idee, diesen Platz dem VIC durch geschicktes Umschalten in den 24-Zeilen-Modus (durch Löschen des Bit ROW SELECT im Register $D011) als nutzbaren Raum zu entlocken. Die Euphorie über diese Entdeckung wurde jedoch rasch wieder gebremst: Außer dem einen Byte am Ende des VIC-Adreßraums, das nur langweilige Streifen erzeugt, kann der VIC in diesem Bereich nur Sprites darstellen. So lag die einzige Nutzung dieses zusätzlichen Raumes darin, ein paar einsame Sprites über den Rand hinauswandern zu lassen. Diese Sprites können aber auch sinnvoller eingesetzt werden!
Bei einigermaßen geschicktem Einsatz der acht zur Verfügung stehenden Sprites läßt sich eine brauchbare Laufschrift auf dem Rahmen erzeugen. Es sind aber durchaus auch zwei Laufschriften möglich. Genau dies tut nun das vorliegende Programm (Listing 3, bitte mit dem MSE eingeben). Es scrollt acht Sprites, deren Inhalt in $3E00 bis $3FFF abgelegt ist, von rechts nach links über den Bildschirm. Das dazugehörige kleine Basic-Programm (Listing 4) berechnet aus den eingegebenen Texten die Daten für diese Sprites und schreibt sie gleich in den richtigen Bereich.
Nachdem das Basic-Programm etwa zehn Sekunden lang gerechnet hat, erscheinen die eingegebenen Texte als Laufschriften ober- und unterhalb des Bildschirms. Ein erneutes Einschalten ist nach <RUN/STOP+RESTORE> durch SYS 49152 möglich, da die Daten im Speicher erhalten bleiben. Achtung: Vor dem erneuten Starten des Basic-Teils muß die Interrupt-Routine durch <RUN/STOP+RESTORE> oder durch Eingabe von »SYS 58648:SYS 64789:SYS 64931« (klappt nicht immer) abgeschaltet werden.
Mögliche Änderungen des Programms: Am einfachsten läßt sich die Farbe der Schrift ändern. Hierzu ruft man die Routine nur mit einer anderen Cursorfarbe auf. Zum Verschieben der Laufschriften in Y-Richtung muß der Inhalt der Speicherstelle 49174 verändert werden (normal = 8). In Adresse 49206 steht die Blocknummer des letzten Sprites (normal = 255). Wird diese verändert, müssen natürlich auch im Basic-Teil die Zahlen 15911 und 15872 in Zeile 270/280 verändert werden. Auch mit einem geänderten Zeichensatz läßt sich die Laufschrift einsetzen. Dazu muß die Variable ZA in Zeile 180 auf den Beginn des neuen Zeichensatzes zeigen. Setzt man hier 55296 ein, erscheint die Laufschrift in Kleinschrift.
(Niklas Nebel/dm)PROGRAMM : LAUFSCHRIFT C000 C0B0 ----------------------------------- C000 : 78 A9 5C A0 C0 8D 14 03 47 C008 : 8C 15 03 A9 81 8D 1A D0 A3 C010 : A9 00 8D 0E DC A9 08 A2 5F C018 : 0F 9D 00 D0 CA CA 10 F9 47 C020 : A9 18 A2 00 9D 00 D0 18 CB C028 : 69 30 E8 E8 E0 10 D0 F4 BC C030 : A9 E0 8D 10 D0 A0 FF A2 06 C038 : 07 98 9D F8 07 AD 86 02 0E C040 : 9D 27 D0 88 CA 10 F2 A9 02 C048 : FF 8D 17 D0 8D 1D D0 8D 0E C050 : 15 D0 58 60 01 02 04 08 30 C058 : 10 20 40 80 AD 19 D0 8D 9A C060 : 19 D0 AD 12 D0 C9 F7 90 EB C068 : 0D A9 31 8D 12 D0 A9 13 BC C070 : 8D 11 D0 4C 81 EA A9 F7 49 C078 : 8D 12 D0 A9 1B 8D 11 D0 7C C080 : A2 0E A0 07 DE 00 D0 BD DF C088 : 00 D0 C9 FF F0 08 CA CA 73 C090 : 88 10 F1 4C 31 EA AD 10 67 C098 : D0 59 54 C0 8D 10 D0 39 51 C0A0 : 54 C0 F0 EA A9 7F 9D 00 FB C0A8 : D0 D0 E3 00 06 52 78 14 D6
100 rem ***************************
110 rem * *
120 rem * demo - laufschrift *
130 rem * *
140 rem * von n.nebel 21.2.1986 *
150 rem * *
160 rem ***************************
170 rem
180 poke56,62:clr:za=53248
190 print"{clr}{down}2 texte eingeben, jeweils hoechst. 24 z."
200 input"1.";a$:input"2.";b$
210 print"{clr}"a$:print"{home}{down}{down}"b$
220 fori=15872to16382:pokei,0:next
230 poke56334,0:poke1,51
240 fora=0to7:forb=0to2
250 ad=za+8*peek(1024+a*3+b)
260 a2=za+8*peek(1104+a*3+b)
270 fori=0to7:poke15911+a*64+b+i*3,peek(ad+i)
280 poke15872+a*64+b+i*3,peek(a2+i):next
290 next:next
300 poke1,55:poke56334,1
310 poke53280,0:poke16383,255:sys49152