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.