7. Geräte-I/O
Die I/O (dies
ist die Abkürzung für Input/Output) ist und bleibt eines der
schwierigsten und umfangreichsten Themen beim C64. In gewisser Weise gehören auch
der VIC und der SID zur I/O, aber aufgrund ihrer enormen Wichtigkeit werden
diese Geräte meistens separat behandelt. Ohne VIC geht gar nichts, und ohne SID
werden zumindest Spiele langweilig.
7.1 Die Tastatur
Leider haben Sie auf
diese Weise die Rechnung ohne das im Endeffekt wichtigste Gerät überhaupt
gemacht: Die Tastatur. Ohne dieses Gerät können Sie nicht einmal ein Spiel
laden, und der C64 verfügt auch nicht - wie sein „größerer Bruder“ Amiga - über
ein Kick-ROM, das einfach Programme von Diskette booten kann, die dann auch
ausschließlich nur den Joystick oder die Maus benutzen.
Aber wie wird die
Tastatur überhaupt angesprochen, wenn es scheinbar nicht einmal einen
Tastatur-Chip gibt, der z.B. den ASCII-Code einer gedrückten Taste (bzw. einen Scancode) in eine bestimmte Speicherstelle schreibt, die
dann am besten den Wert 0 enthält, wenn keine Taste gedrückt wurde? In der Tat
gibt es einen solchen Chip nicht, denn die C64-Tastatur ist im Endeffekt eine
moderne Form einer Folientastatur: Immer, wenn eine Taste gedrückt wird, dann
werden von jeweils 8 Eingangsleitungen und 8 Ausgangsleitungen zwei
entsprechende Leitungen miteinander verbunden (sprich auf Masse gelegt), und
den dadurch verursachten Spannungsabfall kann man feststellen. Es gibt nun
8*8=64 Möglichkeiten, die Eingangspins und Ausgangspins miteinander zu
verbinden, und da es 64 Tasten gibt, ist alles in Butter (Die RESTORE-Taste
zählt hier nicht mit, da sie direkt mit einem Interrupt-Pin des 6510 verbunden
ist).
Der Chip, der auch die
Tastatur steuert (und auch weitere Geräte, wie z.B. die Datasette,
den Drucker und die Floppy) ist der CIA-Chip (Complex
Interface Adapter). Im Endeffekt gibt es hiervon sogar zwei
Stück, und der erste (nämlich CIA1) steuert die Tastatur, die auch
direkt an den CIA-Chip Nr. 1 angeschlossen ist. Hierbei bildet dann die Adresse
56320 die Eingangsleitungen ab (zumindest im Standardbettrieb), und
die Adresse 56321 die Ausgangsleitungen. Immer, wenn auf der
Tastatur eine Taste gedrückt wird, dann fließt bei Aktivierung einer
Eingangsleitung (z.B. der ersten) der Strom auf eine bestimmte Ausgangsleitung,
was dann bewirkt, dass in Adresse 56321 das entsprechende Bit
gelöscht wird, das zu der Leitung gehört. Der Tastaturtreiber, der direkt
nach Einschalten des C64 vom Kernal gestartet wird,
macht nun periodisch folgendes:
1. Am Anfang wird Bit 7 in
Adresse 56320 auf 0 gesetzt
2. Es wird nun geschaut,
ob in Adresse 56321 ein Bit gelöscht ist. Wenn dies der Fall ist, dann wird zu
Schritt 5 gesprungen
3. Wenn kein Bit in
Adresse 56321 gelöscht ist, dann werden die Schritte 1 und 2 für das nächst
niedrige Bit wiederholt, das außer allen anderen Bits auf 0 gesetzt wird (im
Endeffekt wird so die entsprechende Eingangsleitung auf Masse gezogen, sodass
hier ein Strom fließen kann)
4. Wenn nun alle Bits
„durchgeschoben“ worden sind, aber kein Bit in Adresse 56321 auf 0 gesetzt ist,
dann wurde keine Taste gedrückt, und der Tastaturtreiber kehrt zum Kernal zurück.
5. Wenn eine Taste
gedrückt wurde, dann wird anhand einer internen Tastaturmatrix im ROM die
entsprechende Taste ermittelt, und anschließend in den Tastaturpuffer (Adresse
640-649) geschrieben. BASIC kann die entsprechende Taste nun als ASCII-Code
„abholen“.
Haben Sie nun gut
aufgepasst? Ja, wahrscheinlich - und festgestellt, dass auch die Adressen der
Joystick-Ports bei 56320 und 56321 liegen. Aber halt: Das würde ja bedeuten,
dass es gar keine Joystick-Ports gibt, und ein Joystick über die Tastatur
(genauer: CIA 1) „eingeschliffen“ wird. Und genau so ist es. Deshalb können
auch bestimmte Joystickbewegungen die Tastatur beeinflussen, was auch immer
wieder als Manko gesehen wurde. Am besten, Sie benutzen den Joystick und die
Tastatur überhaupt nicht gleichzeitig, denn dies kann sogar Ihren CIA
zerstören!
Wenn Sie an dieser
Stelle schon etwas im Internet recherchiert haben (vielleicht können Sie sogar
schon etwas Assembler), dann wundern Sie sich vielleicht darüber, dass manche
Foren die Ein- und Ausgabeleitungen in der umgekehrten Weise beschriften- was schlicht
und einfach falsch ist. In der Elektrotechnik werden immer die Leitungen, die
zur Masse hinführen (stromführende Pins), als Input-Pins, und die
Pins, die hinter der Taste liegen, die auf Masse gelegt wird, als Output-Pins
bezeichnet. Die Input-Pins erscheinen dann als einzelne gelöschte Bits an
Adresse $DC00 (also 56320), die Output-Pins als einzelne gelöschte Bits an
Adresse $DC01 (also 56321). Wenn Sie nun den Status der Tastatur mit einem
eigenen Assemblerprogramm abfragen wollen, dann müssen Sie folgendes tun:
·
Den
Akkumulator mit dem Wert $FE initialisieren
·
Den
Wert des Akkumulators in die Adresse $DC00 übertragen
·
In
Adresse $DC01 nachschauen, ob dort ein anderer Wert steht, als $FF. Wenn ja,
dann wurde eine Taste gedrückt, und der Wert in $DC01 bestimmt die Taste, die
gedrückt wurde.
·
Wenn
an der Adresse $DC01 der Wert $FF steht, und der Akkumulator noch nicht 7-mal
nach links rotiert wurde, dann wird der Inhalt des Akkumulators mit ROL um 1
Bit nach links rotiert, und das Programm kehrt zu Schritt 2 zurück.
·
Wenn
am Ende in Adresse $DC01 ein anderer Wert steht, als $FF, dann wurde eine Taste
gedrückt. Die Eingabeleitung, die zuletzt aktiv war, bestimmt dann zusammen mit
der Ausgabeleitung, die zuletzt auf Masse gezogen wurde, die eigentliche Taste,
die zurzeit gedrückt ist.
Am Ende sollten
sämtliche Eingabeleitungen wieder deaktiviert werden
Das eigentliche
Assemblerprogramm sieht dann so aus:
10 - .BA
49152
20 - LDA
#$FE
30 - LDX
#0
40 -BACK STA
$DC00
50 - NOP
60 - NOP
70 - NOP
80 - LDY
$DC01
90 - CPY
#$FF
100 - BNE
CONT
110 - INX
120 - CPX
#8
130 - BEQ
CONT
150 - ROL
160 - JMP
BACK
170 -CONT STX
1000
180 - STY
1001
190 - LDA
#$FF
200 - STA
$DC00
210 - RTS
Wegen einer kleinen
Latenz des CIA-Chips müssen drei NOP-Befehle folgen, nachdem die Adresse $DC00
beschrieben wurde. Am Ende der Programms befindet sich
nun die Nummer des Eingangspins, der zuletzt aktiv war, in der Speicheradresse
1000, aber es befindet sich nicht die Nummer des Ausgangspins, der zuletzt
gegen Masse gezogen wurde, in der Adresse 1001, sondern der Status sämtlicher
Ausgangsleitungen. Dies liegt daran, dass Sie hier mit einer Bitmaske arbeiten, und nicht direkt mit Pin-Nummern.
Sie müssen also eine zweite Schleife konstruieren, die auch die Pin-Nummer der Ausgabeleitung ermittelt. Wandeln Sie dazu
das obige Programm wie folgt ab:
190 - LDX #0
200 - LDA
#1
210 -BACK2 AND
1001
220 - BEQ
EXIT
230 - INX
240 - ASL
250 - JMP
BACK2
260 -EXIT STX
1001
270 - RTS
Das X-Register wird
also zur Ermittlung der Pinnummer der Ausgabeleitung
erst einmal auf 0 gesetzt, und der Akkumulator mit dem Wert 1 geladen (dies
entspricht einem gesetzten Bit 0 in Adresse 1001). In Zeile 210 wird nun
eine AND-Maske angewendet, die immer dann 0 ist, wenn der entsprechende
Pin auf Masse gezogen wurde. In diesem Fall ergibt der BEQ-Vergleich in Zeile
220 den Wert 0, und die Schleife zur Ermittlung der Pin-Nummer
wird beendet. Ansonsten wird X um 1 erhöht (Prüfung des Pins mit der nächst höheren
Pinnummer), und die Bitmsake
wird ein Bit weiter nach links verschoben. Da vorher garantiert wurde, dass
direkt nach einem Tastendruck mindestens ein Bit in Adresse 1001 gelöscht ist,
bricht die Schleife zur Ermittlung der Pinnummer auch
immer korrekt ab (die Pinnummer steht anschließend in
Adresse 1001).
Nun müssen Sie noch die
eigentliche Taste ermitteln, am besten als ASCII-Code. Dies ist aber nicht so
schwer, wenn Sie erst einmal die Pinnummer der Ein-
und Ausgabeleitungen ermittelt haben, die beim letzten Tastendruck aktiv waren.
Sie können anschließend die folgende Tabelle zu Raten ziehen, die z.B. auch in
dem Handbuch für Ihren C64 abgedruckt ist (Spalten: Bits für $DC00, Zeilen:
Bits für $DC01):
|
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
0 |
1 |
Pfund |
+ |
9 |
7 |
5 |
3 |
DEL |
|
1 |
ß |
* |
P |
I |
Y |
R |
W |
RET |
|
2 |
CTRL |
; |
L |
J |
G |
D |
A |
CSR right |
|
3 |
2 |
HOME |
- |
0 |
8 |
6 |
4 |
F7 |
|
4 |
SPC |
SHIFT |
. |
M |
B |
C |
Z |
F1 |
|
5 |
C= |
= |
: |
K |
H |
F |
S |
F3 |
|
6 |
Q |
^ |
@ |
O |
U |
T |
E |
F5 |
|
7 |
RUN/ STOP |
/ |
, |
N |
V |
X |
SHIFT left |
CSR down |
Normalerweise liegt die
Tastatur-Dekodierungstabelle im Kernal-ROM-Bereich,
und kann deshalb nicht geändert werden. Wenn Sie also eine eigene Tabelle
anlegen wollen (z.B. für Umlaute in Ihrem eigenen Schreibprogramm), dann müssen
Sie das obige Programm abändern. Leider führt dies nun endgültig zu weit, denn
Assembler kommt ja im BASIC-Kurs eigentlich nicht vor. Ich will Sie auch nicht
weiter damit quälen, sondern Ihnen nur zeigen, dass die Abfrage der Tastatur
beim C64 keine Trivialität ist.
7.2 Der Joystick-Port
Haben Sie gut
aufgepasst? Ja, wahrscheinlich - und festgestellt, dass auch die Adressen der
Joystick-Ports bei 56320 und 56321 liegen. Die Joysticks sind aber nur
„eingeschliffen“, deshalb funktionieren diese auch an der Ein- und
Ausgabeleitung des CIA1 in gleicher Weise: Immer, wenn der Joystick bewegt
wird, dann wird ein bestimmter Schalter geschlossen und ein entsprechender Pin
auf Masse gelegt. Deshalb ist es beim Joystick egal, ob sich dieser an den Ein-
oder Ausgangspins der Tastatur befindet. Commodore
hat nun festgelegt, dass der Standard-Joystick in Port 1 an den Ausgangspins
angeschlossen wird, deshalb erscheint Joystickport
1 an Adresse 56321 und der zusätzliche Joystickport
2 an Adresse 56320 (ja, man hätte es auch anders herum machen
können, aber nun ist es einmal so herum, dass es den Programmierer auch
verwirren kann). Die Pins der Joystickports sind nun
in der folgenden Weise verdrahtet (Port 2 wird wie Port 1 verdrahtet)
· Pin 0 (Bit 0) wird mit
dem Schalter für „Joystick nach oben“ verbunden
·
Pin
1 (Bit 1) wird mit dem Schalter für „Joystick nach unten“ verbunden
·
Pin
2 (Bit 2) wird mit dem Schalter für „Joystick nach links“ verbunden
·
Pin
3 (Bit 3) wird mit dem Schalter für „Joystick nach rechts“ verbunden
·
Pin
4 (Bit 4) wird mit dem Schalter für „Feuer“ verbunden (bei neueren Joysticks
ist dies die linke Feuertaste)
·
Pin
5 (Bit 5) kann mit einem Schalter für „Feuer“ verbunden werden (bei neueren
Joysticks ist dies die rechte Feuertaste
Nun verstehen Sie
wahrscheinlich auch, warum Sie bei der Joystickabfrage in BASIC-Programmen so
ein eigenartiges Konstrukt mit IF NOT PEEK benutzen müssen: Sie müssen
feststellen, ob ein bestimmtes Bit gelöscht ist, und deshalb müssen Sie eben NOT
hinter IF benutzen, zusammen mit einer AND-Maske, die alle
anderen Bits außer das abzufragende Bit ausmaskiert. Auf diese Weise erhalten
Sie das bereits bekannte Konstrukt:
10 PO=56320:REM PO=56321:PORT 1, PO=56320:PORT 2
20
IF NOT PEEK(PO) AND 1 THEN GOSUB OBEN
30 IF NOT PEEK(PO) AND 2 THEN GOSUB UNTEN
40 IF NOT PEEK(PO) AND 4 THEN GOSUB LINKS
50 IF NOT PEEK(PO) AND 8 THEN GOSUB RECHTS
60 IF NOT PEEK(PO) AND 16 THEN GOSUB FEUER
Das Assembler-Pendant
ist übrigens das folgende Programm (dies dient hier wieder nur als kleines
Schmankerl, mit dem Sie Ihre Programme richtig beschleunigen können):
10 - .BA
49152
20 - .EQ JOY1 56321
30 - .EQ JOY2 56320
40 - LDA
JOY1
50 - AND
#$01
60 - BNE
OBEN
70 - LDA
JOY1
80 - AND
#$02
90 - BNE
UNTEN
100 - LDA
JOY1
110 - AND
#$04
120 - BNE
LINKS
130 - LDA
JOY1
140 - AND
#$08
150 - BNE
RECHTS
160 - LDA
JOY1
170 - AND
#$10
180 - BNE FEUER
7.3 Mäuse für den C64
Es gibt
unterschiedliche Mäuse für den C64. Die einfachste Bauart ist eine Maus, die
einfach anstatt eines Joysticks an Port 1 angeschlossen wird. Diese Art Maus
funktioniert dann auch wie ein Joystick, nur wird hier jedes Mal, wenn die Maus
in eine bestimmte Richtung bewegt wird, das entsprechende Bit in Adresse 56321
gelöscht. Dies kann allerdings sehr viele Aufrufe des entsprechenden
Unterprogramms in einer sehr kurzen Zeit bewirken, deshalb muss es für solche
Mäuse auch Treiberprogramme geben, die in Maschinensprache geschrieben werden
(unter Umständen müssen Sie dies sogar selbst erledigen).
7.3.1 Die Standardmäuse
1350 und 1351
Die Standardmaus für
den C64 ist hingegen die Commodore-Maus 1350 oder der
bessere Nachfolger 1351. Diese Maus wird zwar anstatt eines Joysticks an Port 1
angeschossen, benutzt aber noch zusätzliche Pins an der Anschlussbuchse für
Port 1. Port 1 ist nämlich nicht nur mit der Tastatur verbunden, sondern auch
mit den analogen Eingängen des SID, die ursprünglich dafür gedacht sind,
Stereo-Sounds mit einer Auflösung von 8 Bits zu samplen. Da aber der Speicher
des C64 nur für sehr kurze Samples ausreicht, wird SID-Register Nr. 25
oft für die X-Koordinaten und SID-Register Nr. 26 für die Y-Koordinaten
von analogen Joysticks „missbraucht“. Die C64-Mäuse 1350 und 1351 verwenden nun
SID-Register 25 und 26 dafür, die relativen Koordinaten der
letzten Mausbewegung als Spannungen an den SID weiterzureichen, der dann diese
Spannungen wieder zurück in relative Bewegungsdaten wandelt. Leider lässt die
geringe Auflösung des SID nur Distanzwerte zwischen 0 und 63 Pixeln zu,
deswegen benötigt man hier spezielle Treiber in Form von Maschinenprogrammen,
damit man vernünftig mit der Maus arbeiten kann. In meinem Sprite-Editor und
meinem Zeichen-Editor habe ich einen solchen Treiber integriert, aber das
entsprechende Assembler-Programm kann ich Ihnen nur in einem separaten
Assembler-Kurs näherbringen.
7.4 Timer
Zeitmessungen sind mit
dem C64 nicht trivial, deswegen muss ich an dieser Stelle auch etwas ausholen.
Es gibt verschiedene Arten, die Zeit zu messen. Die einfachste Variante ist,
einfach den Rasterstrahl des C64 zur Hilfe zu nehmen, der etwa 60-mal in der
Sekunde ein Interrupt auslöst. Ein Interrupt ist eine kurze
Unterbrechung des BASIC-Interpreters durch ein externes Ereignis, wie z.B. der
Tatsache, dass der Rasterstrahl gerade in Zeile 0 zurückkehrt. An dieser Stelle
spricht man auch von einem Raster-Interrupt. Mit dem Raster-Interrupt
die Zeit zu messen ist sehr einfach, denn Sie müssen nur eine Interrupt-Routine
programmieren, die jedes Mal einen Zähler um 1 erhöht, wenn sie aufgerufen wird
(und dies geschieht automatisch ohne Ihr Zutun). Wenn Sie nun noch beachten,
dass Sie hier 1/60 Sekunden zählen, dann können Sie sogar mit BASIC den
entsprechenden Zähler in das Format HH:MM:SS (hours, minutes, seconds) umwandeln. Wie Sie aber bereits wissen, gibt es
verschiedene Versionen des C64 (z.B. NTSC- und PAL-Version), deswegen wird der
Raster-Interrupt nicht wirklich genau 60-mal in der Sekunde aktiviert. Dies ist
dann auch der Grund dafür, dass solche einfachen Uhren mit der Zeit nachgehen.
7.4.1 Der Timer-Interrupt
Der Timer-Interrupt wird vom CIA1
aktiviert, und dies geschieht 60-mal in der Sekunde. Dazu benutzt der CIA1 aber
den Prozessortakt, deshalb geht auch der Timer-Interrupt
nach (der Prozessortakt ist eben nicht genau 1 MHz). Da die BASIC-Uhr den Timer-Interrupt benutzt, geht
natürlich auch die BASIC-Uhr nach.
Für Spiele reicht die
BASIC-Uhr oder sogar der Raster-Interrupt aus, denn meistens will der Spieler
nicht die genaue Uhrzeit wissen, sondern nur, wie viele Punkte und Leben er
hat. Auch ein im Hintergrund laufender Song wird meistens an den raster-Interrupt
oder an den Timer-Interrupt
gebunden. Vielleicht ist dies auch der Grund, warum man die CIA-Chips nie
besonders stark ausreizt.
7.4.2 Die
CIA-Zeitzähler
Der CIA1-Chip kann jedoch
viel mehr, als man im Allgemeinen in Internet-Foren erfährt, denn er enthält
zusätzlich zu den Tastatur-Anschlusspins verschiedene Zeitzähler, die Sie sogar
durch entsprechende POKE-Befehle steuern können. Hierzu müssen Sie sich
wieder zahlreiche Adressen und Registernummern merken. Das erste, dass Sie sich
merken müssen, ist, dass die Basisadresse des CIA1 56320 ist und die
Basisadresse des zweiten CIA-Chips 56336. Wie auch schon beim VIC
und SID können Sie mit PEEK und POKE auf die Steuerregister der
CIA-Chips zugreifen. Leider ist dies nicht trivial, und im Endeffekt kann man
ganze Bücher nur über die CIA-Chips im C64 schreiben. Der BASIC-Kurs stößt hier
also an seine Grenzen, ich will Ihnen jedoch eine Tabelle mit sämtlichen
CIA-Steuer-Registern nicht vorenthalten. Quelle: https://www.c64-wiki.de/wiki/CIA
CIA 1:
Adressbereich:
$DC00-$DCFF, 56320-56575
Aufgaben: Tastatur,
Joystick, Paddles, Datasette, Userport,
IRQ-Steuerung
|
Adresse |
Registernummer |
Funktion |
|
56320 |
0 |
Daten-Port A Abfrage/Steuerung der Datenleitungen
von Port A Die Leitungen sind mehrfach belegt: Schreiben: Bit 7-0: Auslesen der
Tastaturmatrixspalten in Register 1 Lesen: Joystick Port 1, Zustand der
Joystickschalter (es können auch mehrere Richtungen gleichzeitig aktiviert
sein) Bei Paddles ist Bit 2 und 3 für
Feuerknöpfe reserviert, Bit 6 und 7 dient der Umschaltung (01=Paddle A,
10=Paddle B), allerdings müssen hier die Koordinaten aus SID-Register 25 und
26 ausgelesen werden |
|
56321 |
1 |
Daten-Port B Abfrage/Steuerung der acht
Datenleitungen von Port B. Ein Schreiben oder Auslesen dieses
Ports bewirkt die Ausgabe eines Low-Pulses auf die Handshake-Leitung PC für
die Dauer eines Systemtaktzyklus (beim C64 nicht verwendet). Die Leitungen
sind mehrfach belegt: Lesen/Schreiben: Bit 7-0 der Tastaturmatrixzeilen
(normalerweise ist dieses Register auf INPUT geschaltet, dies kann aber auch
geändert werden) Für Joysticks und Lightpens
gilt: Lesen: Lightpen Bit 4 (wie
Feuerknopf), auch mit LP (Pin 9) des VIC verbunden und kann auch ein
Interrupt auslösen. Lesen: Bit 7: Timer
B Toggle/Inpulsausgang Bit 6: Timer A Toggle/Impulsausgang Bit 4: Joystick 2 Feuer Bit 3: Joystick 2 rechts Bit 2: Joystick 2 links Bit 1: Joystick 2 unten Bit 0: Joystick 2 oben |
|
56322 |
2 |
Datenrichtung Port A Bit X: 0=Eingang (nur lesen), 1=Ausgang (lesen
und schreiben) |
|
56323 |
3 |
Datenrichtung Port B Bit X: 0=Eingang (nur lesen), 1=Ausgang
(lesen und schreiben) |
|
56324 |
4 |
Timer A Lo Lesen: aktueller Wert Timer
A (Lo-Byte) Schreiben: Latch
des Timers A setzen (Lo-Byte) |
|
56325 |
5 |
Timer A Hi Lesen: aktueller Wert Timer
A (Hi-Byte) Schreiben: Latch
des Timers A setzen Hi-Byte) Falls der Timer
vorher gestoppt wurde, wird gleichzeitig das Lo-Byte des Timers
neu gesetzt |
|
56326 |
6 |
Timer B Lo Lesen: aktueller Wert Timer
B (Lo-Byte) Schreiben: Latch
des Timers B setzen (Lo-Byte) |
|
56327 |
7 |
Timer B Hi Lesen: aktueller Wert Timer
B (Hi-Byte) Schreiben: Latch
des Timers B setzen Hi-Byte) Falls der Timer
vorher gestoppt wurde, wird gleichzeitig das Lo-Byte des Timers
neu gesetzt |
|
56328 |
8 |
Echtzeituhr 1/10s Lesen: Bit 7-4 ist immer 0 Bit 0-3: Zehntelsekunden im BCD-Format
($00-$09) Schreiben: Bit 3-0: Wenn CRB-Bit7=0: Setzen der
Zehntelsekunden im BCD-Format |
|
56329 |
9 |
Echtzeituhr Sekunden Bit 7-4: Zehnersekunden im BCD-Format ($00-$05) Bit 3-0: Einersekunden
im BCD-Format ($00-$09) |
|
56330 |
10 |
Echtzeituhr Minuten Bit 7-4: Zehnerminuten im BCD-Format ($00-$05) Bit 3-0: Einerminuten
im BCD-Format ($00-$09) |
|
56331 |
11 |
Echtzeituhr Stunden Bit 7: 0=AM, 1=PM Bit 6-4: Zehnerstunden im BCD-Format ($00-$01) Bit 3-0: Einerstunden
im BCD-Format ($00-$09) Ein Schreiben in dieses Register
stoppt die Echtzeituhr, bis Register 8 erneut beschrieben wird. Ein Lesen aus diesem Register friert
alle Register der Echtzeituhr ein (die Uhr läuft aber weiter), bis Register 8
erneut gelesen wird. |
|
56332 |
12 |
serielles Schieberegister Am SP-Pin wird das Byte in diesem
Register bitweise mit jeder positiven Flanke am CNT-Pin eingelesen bzw.
hinausgeschoben (bei letzterem kann CNT auch von TIMER A- Unterläufen
getrieben sein). Die Signale SP und CNT sind am Userport (Pin 5 bzw. 4) herausgeführt. |
|
56333 |
13 |
Interrupt Control und Status CIA1 ist mit der IRQ-Leitung der CPU
verbunden. Lesen: Bit 7=1: Ein Interrupt ist
aufgetreten, also Übereinstimmung mindestens eines Bits von INT MASK und INT
DATA Bit 6-5: Immer 0 Bit 4-0: INT DATA (Herkunft des
Interrupts): Bit 4=1: Am FLAG-Pin
(Handshake-Leitung) ist eine negative Flanke aufgetreten (Kassettenport
D4, READ-Leitung, Serielle Schnittstelle SRQ IN) Bit 3=1: SDR (Schieberegister)
vollständig abgearbeitet, also wurde ein vollständiges Byte übertragen,
abhängig von der Betriebsart des Schieberegisters (lesend, schreibend) Bit 2=1: Gleichheit von Uhrzeit und
Alarmzeit Bit 1=1:Unterlauf
Timer B Bit 0=1:Unterlauf
Timer A Die Bits in diesem Register werden
durch das Lesen gelöscht. Schreiben: Bit 7: Quellbit Bit=0: Jedes gesetzte Bit (4-0) löscht das
entsprechende Masken-Bit. Bit=1: Jedes gesetzte Bit (4-0) setzt das
entsprechende Masken-Bit. Bit 6-5: Nicht benutzt Bit 4-0: INT MASK (Interruptmaske) Bit 4=1: Interruptfreigabe
für das Erkennen einer negativen Flanke am FLAG-Pin. Bit 3=1: Interruptfreigabe für das Ende der
Übertragung eines kompletten Bytes über das serielle Schieberegister. Bit 2=1: Interruptfreigabe für
Uhrzeit-Alarmzeit-Übereinstimmung. Bit 1=1: Interruptfreigabe für Timer B Unterlauf. Bit 0=1: Interruptfreigabe für Timer A Unterlauf. |
|
56334 |
14 |
Control Timer A Bit 7: Echtzeituhr, 0=60 Hz, 1=50 Hz an Pin 19 Bit 6: Richtung des seriellen
Schieberegisters, 0=SP-Pin ist Eingang (lesen), 1=SP-Pin ist Ausgang
(schreiben) Bit 5: 0=Timer
wird mit der Systemfrequenz getaktet, 1=Timer wird
von positiven Flanken am CNT-Pin getaktet Bit 4: 1=Einmalig Latch
in den Timer laden Bit 3: 0=Timer-Neustart
nach Unterlauf (Latch wird neu geladen), 1=Timer stoppt nach Unterlauf Bit 2: 0: Bei einem Timer-Unterlauf
wird an Port B das Bit 6 invertiert 1: Bei einem Timer-Unterlauf
wird an Port B das Bit 6 für einen Systemtaktzyklus auf 1 gesetzt Bit 1: 1=Zeigt einen Timer Unterlauf an Port B in Bit 6 an Bit 0: 0=Stop Timer, 1=Start Timer |
|
56335 |
15 |
Control Timer
B Bit 7: 0 = Schreiben in die
TOD-Register setzt die Uhrzeit, 1 = Schreiben in die TOD-Register setzt die
Alarmzeit Bit 6-5: Timer
wird getaktet: 00 = mit der Systemfrequenz 01 = von positiver Flanke am CNT-Pin 10 = vom Unterlauf des Timer A 11 = vom Unterlauf des Timer A, wenn CNT-Pin High ist Bit 4: 1=Einmalig Latch
in den Timer laden Bit 3: 0=Timer-Neustart
nach Unterlauf (Latch wird neu geladen), 1=Timer stoppt nach Unterlauf Bit 2: 0: Bei einem Timer-
Unterlauf wird an Port B das Bit 6 invertiert 1: Bei einem Timer-Unterlauf
wird an Port B das Bit 6 für einen Systemtaktzyklus auf 1 gesetzt Bit 1: Zeigt einen Timer
Unterlauf an Port B in Bit 7 an, wenn es gesetzt ist Bit 0: 0=Stop Timer, 1=Start Timer |
CIA 2:
Aufgaben: Serielle
Schnittstelle, RS-232, VIC Speicheradressierung, Userport, NMI-Steuerung
Der zweite CIA-Chip (Adressen
56336-56831) ist identisch mit dem ersten. Deshalb sind in der folgenden
Tabelle nur Einträge zu finden, die sich auf die spezifische Verwendung im C64
beziehen.
|
Adresse |
Registernummer |
Funktion |
|
56576 |
0 |
Daten-Port A CIA2 Bit 0 und 1 dienen
der Auswahl der Position des VIC-Speichers: 00, 0: Bank 3:
$C000-$FFFF, Adressen 49152-65535 01, 1: Bank 2:
$8000-$BFFF, Adressen 32768-49151 10, 2: Bank 1:
$4000-$7FFF, Adressen 16384-32767 11, 3: Bank 0:
$0000-$3FFF, Adressen 0-16383 (Standard) Bit 7-2 dienen zur
Steuerung der seriellen Schnittstelle: Bit 7-6: Serielle
Schnittstelle Eingang (Lo Active) Bit 7=DATA IN (DATA LO=1-Bit, DATA HI=0-Bit, Clock Hi=BIT PRESENT) Bit 6=CLOCK IN Bit 5-3: Serielle
Schnittstelle Ausgang (Lo Active) Bit 5: DATA OUT Bit 4: CLOCK OUT Bit 3: ATN OUT (Attention-Leitung, z.B. für Floppys) Bit 2: RS-232: TXD Ausgang, Userport-Daten PA 2 (Pin M) |
|
56577 |
1 |
Daten-Port B CIA 2 Bit 7-0: Userport
Daten PB 0-7 (Pins C,D,E,F,H,J,K,L), außerdem Userport (Pin 8) Handshake-Leitung PC, die bei jedem
Schreiben oder Lesen des Ports aktiv wird. Der KERNAL bietet einige
RS232-Routinen an, die die Pins folgendermaßen benutzen: RS-232 lesen: Bit 7: DSR Bit 6: CTS Bit 5: Userport Pin J Bit 4: DCD Bit 3: RI Bit 0: RXD RS-232 schreiben: Bit 5: Userport Pin J Bit 4: DCD Bit 3: RI Bit 2 DTR Bit 1: RTS |
|
56578 |
2 |
Datenrichtung Port A Siehe CIA 2 |
|
56579 |
3 |
Datenrichtung Port B Siehe CIA 1 |
|
56580 |
4 |
Timer A Lo Siehe CIA 1 |
|
56581 |
5 |
Timer A Hi Siehe CIA 1 |
|
56582 |
6 |
Timer B Lo Siehe CIA 1 |
|
56583 |
7 |
Timer B Hi Siehe CIA 1 |
|
56584 |
8 |
Echtzeituhr 1/10s Siehe CIA 1 |
|
56585 |
9 |
Echtzeituhr Sekunden Siehe CIA 1 |
|
56586 |
10 |
Echtzeituhr Minuten Siehe CIA 1 |
|
56587 |
11 |
Echtzeituhr Stunden Siehe CIA 1 |
|
56588 |
12 |
serielles Schieberegister Die dazugehörigen Signale SP und CNT
sind am Userport (Pin 7 bzw. 6) herausgeführt
(siehe CIA 1) |
|
56589 |
13 |
Interrupt Control und Status CIA 2 ist an die NMI-Leitung der CPU
angeschlossen uns löst folgende Interrupts aus: Bit 7: 1, wenn Ein Interrupt aufgetreten ist , also bei einer Übereinstimmung mindestens eines Bits
von INT MASK und INT DATA am CPU. Bit 4: 1: NMI auslösen, wenn eine negative
Flanke am FLAG-Pin aufgetreten ist (RS-232 Daten empfangen, am Userport, Pin B, herausgeführt) CIA1 ist ebenfalls mit der IRQ-Leitung
der CPU verbunden. |
|
56590 |
14 |
Control Timer A Siehe CIA 1 |
|
56591 |
15 |
Control Timer B Siehe CIA 1 |
7.5 Ändern der Speicherkonfiguartion
Wie Sie der Tabelle aus
7.4 entnehmen können (zumindest wenn Sie diese
aufmerksam studieren, was aber einige Zeit benötigt), dann werden Sie sehen,
dass CIA2 auch mit einigen Prozessor-Pins verbunden ist, die für die
Speicheraufteilung verantwortlich sind. So können Sie z.B. die Position der
VIC-Register oder des Grafikspeichers verschieben. Wahrscheinlich verstehen Sie
nun auch, warum die Routine für das Kopieren der ROM-Zeichentabelle in das RAM
genau die Adressen benutzt, die es benutzt, und keine anderen.
7.5.1 Mehr
BASIC-Speicher reservieren
Fragt sich nur, warum
Sie die Speicherkonfiguration ändern müssen. Warum müssen Sie z.B. den
Grafikspeicher verschieben? Dies kann jedoch schneller geschehen, als Sie
denken. So ist z.B. die höchst mögliche Adresse, an der Sie im Standardbetrieb
eine eigene Zeichentabelle ablegen können, im Standardbetrieb 14336
(auszuwählen mit POKE 53272,30). Wenn Sie zusätzlich umfangreiche
BASIC-Programme schreiben, dann kann der Speicher knapp, werden, weil
umfangreiche BASIC-Programme schon einmal in den Bereich einer
RAM-Zeichentabelle „vordringen“ können. Auch bei den Sprites
ist dies so, und die beste Lösung ist hier sicherlich das Verschieben des
Grafik-Speichers in den Bereich 16383-32767, der durch folgende BASIC-Zeilen
realisiert werden kann:
POKE
56576,150 (Rückgängig machen mit POKE 56576,151)
7.5.2 Tripple Buffering
Auch echt schnelles,
sogenanntes „Turbo Scrolling“ benutzt Speicherverschiebungs-Techniken für das
sogenannte Tripple Buffering. Tripple Buffering benutzt nicht zwei,
sondern drei Grafikpuffer, und schaltet ständig zwischen ihnen um. Zunächst
wird Puffer 1 auf dem Bildschirm angezeigt, und Puffer 2 kann inzwischen
aktualisiert werden. Durch eine Refresh-Routine wird nun Puffer 2 angezeigt,
während im Hintergrund Puffer 2 in Puffer 1 kopiert wird. Nun kann Puffer 1
aktualisiert werden, und wenn dies geschehen ist, wird wieder Puffer 1
angezeigt und in Puffer 2 kopiert. Es gibt hier also drei Puffer: Der aktuell
angezeigte Bildschirm (Puffer 0), und zwei Puffer (Puffer 1 und 2), zwischen
denen ständig hin und her geschaltet wird. Der Trick ist hier, dass der Wechsel
selbst sofort und ohne Flimmern möglich ist.
7.6 Die serielle
Schnittstelle
Die serielle
Schnittstelle ist im Endeffekt eine Kombination aus Prozessorpins und
Funktionen des CIA2-Chips, deshalb ist auch das Verwalten der seriellen
Schnittstelle nicht trivial. Genau aus diesem Grund nimmt Ihnen BASIC einiges
an Arbeit ab, indem es Dateinummern für die serielle Schnittstelle anbietet
(diese sind allerdings vom Gerät abhängig). Dies bedeutet, dass Sie mit dem
Standard-Befehl OPEN auf die serielle Schnittstelle zugreifen können.
Allerdings ist die serielle Schnittstelle sehr langsam und sendet und empfängt
Daten mit maximal 1.200 Baud. Auch die Floppys und Drucker hängen an der
seriellen Schnittstelle, allerdings alle an der gleichen Leitung. Deshalb
benötigen die meisten Geräte eine zusätzliche Leitung, nämlich die ATN-Leitung
(ATN=attention), und auch zusätzlich Gerätenummern,
die Sie mit DIP-Schaltern an der Geräterückseite konfigurieren müssen. Nur auf
diese Weise können Sie die Geräte gezielt ansprechen. Der C64 benutzt dafür ein
spezielles Protokoll, das auch Commodore-Bus
genannt wird. Um mit diesem Bus einen Befehl an ein Gerät zu senden (z.B. das
Scratch-Kommando an die Floppy), müssen stets die folgenden Schritte
durchgeführt werden:
·
Setzen
der ATN-Leitung am C64 auf Hi. Dies bewirkt, dass Ihre Geräte, die am Bus
hängen, Ihrem C64 nun zuhören.
·
Senden
des Gerätenummer-Bytes über die serielle Schnittstelle (dies geht über
spezielle Kernal-Routinen, die hier die
entsprechenden Register des 2. CIA-Chips in der richtigen Reihenfolge benutzen)
·
Die
ATN-Leitung wieder auf Lo setzen
·
Warten,
bis das Gerät, das Sie ansprechen wollen, die ATN-Leitung wieder auf Hi setzt
(das Gerät ist jetzt bereit, zuzuhören)
·
Senden
der Nummer des Kanals und des Subkanals (dies sind also 2 Bytes)
·
Senden
der Daten (z.B. den Scratch-Befehl über Kanals 15, Unterkanal 1 der Floppy)
·
Warten,
bis das Gerät die ATN-Leitung auf Lo setzt (die Daten wurden übermittelt und
vom Gerät vollständig verarbeitet)
Nachdem ein Gerät Ihnen
vollständig zugehört hat, kann es seinerseits Daten senden. Auch hier wird die
ATN-Leitung wieder auf Hi gesetzt- diesmal allerdings von dem sendenden Gerät,
und nicht vom C64, der nun die Rolle des Zuhörers einnimmt. BASIC übernimmt
auch hier die Rolle des Vermittlers für die wechselnden Rollen am Commodore-Bus, und bildet die internen Abläufe auch auf den
OPEN-Befehl ab. Der OPEN-Befehl ruft seinerseits die richtigen Kernal-Routinen auf. Deshalb verwenden Sie auch nur sehr
selten direkt die CIA-Register, sondern fast immer das Kernal
oder BASIC-Befehle.