CIA-Kurs: "Die Geheimnisse des Secret Service..." (Teil 5)
Hallo zusammen, zum 5 . Teil dieses Kurses. Nachdem Sie nun ja ausgiebig über
Interrupts Bescheid wissen, wollen wir
uns diesen Monat noch einmal ein wenig
intensiver um die CIA-Bausteine ansich
kümmern, denen dieser Kurs ja gewidmet
ist.
Diesmal wollen wir nämlich das Thema der
Timerkopplung behandeln. Darunter versteht man die Verkettung von Timer A und
Timer B einer CIA zu einem großen 32- Bit
Timer ( je ein Timer verfügt ja über je
16 Bit) .
Wozu das gut ist, werden Sie spätestens
dann gemerkt haben, als Sie einmal einen
Timer-Interrupt programmieren wollten, der weniger oft als 15 mal pro Sekunde
auftritt. Dann reicht nämlich ein 16-
Bit-Timer nicht mehr aus, insofern er
Systemtakte zählt, was ja eigentlich die
häufigste Anwendung der Timer-Triggerung
ist ( Triggerung gibt den auslösenden
Faktor an, der den Timer dazu veranlaßt, den Wert, den er beinhaltet um 1 zu erniedrigen - ich erwähnte Timer-Trigger
schon zu Anfang dieses Kurses) .
Sie können in einen 16- Bit-Timer ja einen maximalen Wert von 2↑16-1=65535 laden. Bei 985248 .4 Taktzyklen, die der
64 er pro Sekunde bekommt, heißt das also, daß der Timer genau 985248 .4/65535=15 .03392691 mal pro Sekunde unterlaufen
kann, wenn er am langsamsten läuft.
Langsamer ( oder besser: weniger häufig) geht es nicht.
Zu diesem Zweck besteht nun aber auch
die Möglichkeit Timer A und Timer B einer CIA zu koppeln. Öber Timer B hatten
wir bisher ja wenig gesprochen, da er
vom Betriebssystem sowohl in CIA1, als
auch in CIA2 nicht benutzt wird. Jedoch
ist es ebenso möglich ihn als Timer zu verwenden, wobei analog zu Timer A vorgegangen wird.
Nun jedoch zu jener Kopplung. Es gibt
nämlich eine Möglichkeit, mit der wir
Timer B anstelle von Systemtakten die
Unterläufe von Timer A zählen lassen
können. Das heißt also, daß jedesmal, wenn Timer A bei 0 angekommen ist, Timer
B um 1 erniedrigt wird. Schaltet man nun
einen Interrupt so, daß er dann von einer CIA ausgelöst wird, wenn Timer B
unterläuft, so hat man einen vollen 32- Bit-Zähler, mit dem wir schon ganz andere Dimensionen in Sachen Häufigkeit von
Unterläufen erreichen können. Mit 32 Bit
können wir nämlich maximal 2↑32-1=42949672995( in Worten: über zweiundvierzigmilliarden) Werte zählen, was
bedeutet, daß wir auch dementsprechend
lange Pausen zwischen zwei Timerinterrupts haben. Mal kurz durchgerechnet
sind das alle 42949672955/985248 .2=4359 .273556 Sekunden. Das sind mehr als72 Minuten, also eine ganze Menge!
Die dabei anfallenden Interrupts werden
dann von Timer B ausgelöst, weshalb wir
dann auch darauf achten müssen, das wir
ihn als Interruptquelle im ICR ( Register
13) setzen.
Kommen wir nun zu einer Anwendung. Ich
muß gestehen, viele Möglichkeiten hierzu
bieten sich mir nicht, jedoch könnte
eine Timerkopplung durchaus zur Lösung
des einen oder anderen speziellen Problems nützlich sein. Ich habe mir da
eine ganz sinnvolle Anwendung einfallen
lassen und Ihnen gleich einmal ein Beispielprogramm vorbereitet, anhand dessen
ich Ihnen die Timerkopplung erläutern
möchte. Es ist ein kleines Programm, das
den Takzyklenverbrauch eines anderen
Programms stoppen kann. Es heißt EVAL
und ist auf dieser MD in zwei Versionen
gespeichert. Zum einen habe ich da den
ausführbaren Code, den Sie absolut laden
müssen ( mit ",8,1") und der ab Adresse
$9000( dez.36864) gestartet wird. Hierzu jedoch später mehr. Desweiteren finden
Sie auch noch den Quell-Code von EVAL
unter dem Namen " EVAL. SRC" . Er ist wie
immer im Hypra-Ass- Format und kann auch
ohne HYPRA-ASS mit ",8" zum Anschauen in
den Basicspeicher geladen werden.
Doch nun zu EVAL selbst. Zunächst einmal
wollen wir uns fragen, was nun genau
geleistet werden soll. EVAL soll
zunächst einmal ganz einfach die Taktzyklen zählen, die ein anderes Programm
verbraucht. Das ist die Problemstellung.
Die Lösung wollen wir - na, Sie werden es
nicht glaubenüber die Timer einer CIA
bewerkstelligen. Ich habe zu diesem
Zweck die CIA2 ausgesucht, deren Timer
normalerweise, solange die RS232- Schnittstelle des C64 nicht genutzt
wird, unbenutzt sind.
Zur Ermittlung der verstrichenen Zeiteinheiten, sprich Taktzylen, müssen wir
nun einfach nur einen bestimmten Grundwert in beide Timer laden, sie starten und anschließend das zu prüfende Programm aufrufen. Dies wollen wir mittels
eines " JSR"- Befehls tun. Springt das
aufgerufene Programm nun zurück, so müssen wir den Timer direkt anhalten und
anschließend den in ihm enthaltenen Wert
von unserem Anfangswert subtrahieren.
Dadurch erhalten wir die Anzahl der
Taktztyklen, die verstrichen sind, zwischen Start und Stop des Timers.
Soviel zum theoretischen Programmablauf
von EVAL. Kommen wir nun zu den Timern
selbst. Zunächst müssen wir zusehen, daß
wir eine richtige Triggerung für Timer A
und Timer B wählen. Timer B soll ja die
Unterläufe von Timer A zählen und dieser
widerum die Systemtakte. Zu diesem Zweck
schreiben wir also erst einmal den Wert
$81 in das Control-Register von Timer A
(= CRA, Reg.14), wie wir das ja auch
schon von der Interruptprogrammierung
her kennen. Weil bei diesem Wert Bit 5 gelöscht ist zählt Timer A also System- takte.
Für Timer B wird das schon schwieriger.
Ich hatte Ihnen ja schon einmal bei der
Beschreibung der CIA-Register aufgelistet, welche Möglichkeiten es hier gibt.
Timer B kann nämlich in Gegensatz zu
Timer A vier ( anstelle von zweien) verschiedene Triggerquellen haben. Dies
wird von den Bits 5 und 6 gesteuert, deren Kombinationen ich Ihnen noch einmal auflisten möchte:
Bit 5 6 Timer B zählt...
0 0 Systemtakte. 0 1 steigende CNT-Flanken. 1 0 Unterläufe von Timer A. 1 1 Unterläufe von Timer A, wenn CNT=1 ist.
Für uns kommt da die Kombination "10" in
Frage. Bit 5 ist also gesetzt und alle
anderen gelöscht. Wie bei Timer A müssen
wir jedoch auch Bit 0 setzen, weil wir beim Laden dieses Wertes in das Control-Register von Timer B ( CRB, Reg.15) den
Timer auch gleich starten wollen. Demnach brauchen wir diesmal den Wert $41 .
Das war dann auch schon alles, was wir
zur Timerkopplung brauchen. Timer A
zählt nun Systemtakte und löst bei jedem
Unterlauf ein Herabzählen von Timer B
aus. Einen Interrupt wollen wir diesmal
nicht erzeugen, doch könnte man auch
durchaus im ICR festlegen, das einer
erzeugt werden soll, wenn Timer B dann
unterläuft.
Somit hätten wir also einen 32- Bit Timer
der Systemtakte zählt. Die Reihenfolge
der LO/ HI-Zählbytes sieht nun folgendermaßen aus:
TimerB-HI TimerB-LO TimerA-HI TimerA-LO
Sie müssen also nicht nur ein High und
Lowbytepaar berechnen, sondern gleich
zwei. Hier wechselt man dann auch in die nächsthöhere Ebene der " Bits und Bytes" .
Eine 16- Bit-Zahl bezeichnet man nämlich
als ein " Word"( engl. : wörd = Wort) und
eine 32- Bit-Binärzahl als ein " Longword"( engl. : Langwort) . Um eine Dezimalzahl
nun in ein Longword umzuwandeln müssen
Sie folgendermaßen vorgehen:
1) Zunächst teilen wir unsere Zahl durch
2↑16(=65536) und nehmen den Ganzzahlanteil des Ergebnisses als höherwertiges Word. Dieses wird nun wie gewohnt in Lowund Highbyte aufgespalten.
2) Nun multiplizieren wir das High-Word
mit 2↑16 und subrahieren den Wert von
unserem Anfangswert. Das Ergebnis was
wir hier erhalten ist das niederwertige Word, das ebenfalls, wie gewohnt, in Lowund Highbyte umgewandelt wird.