CIA-Kurs: "Die Geheimnisse des Secret Service..." (Teil 5) ----------------------------------------
Hallo zusammen, zum 5. Teil dieses Kur- ses. 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 ver- steht 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 er- niedrigen - ich erwähnte Timer-Trigger schon zu Anfang dieses Kurses). Sie können in einen 16-Bit-Timer ja ei- nen maximalen Wert von 2↑16-1=65535 la- den. Bei 985248.4 Taktzyklen, die der 64er pro Sekunde bekommt, heißt das al- so, 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 ei- ner 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 vor- gegangen 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 ei- ner CIA ausgelöst wird, wenn Timer B unterläuft, so hat man einen vollen 32- Bit-Zähler, mit dem wir schon ganz ande- re 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 zweiund- vierzigmilliarden) Werte zählen, was bedeutet, daß wir auch dementsprechend lange Pausen zwischen zwei Timerinter- rupts haben. Mal kurz durchgerechnet sind das alle 42949672955/985248.2 = 4359.273556 Sekunden. Das sind mehr als 72 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 Pro- blems nützlich sein. Ich habe mir da eine ganz sinnvolle Anwendung einfallen lassen und Ihnen gleich einmal ein Bei- spielprogramm 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 Taktzy- klen 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 Zei- teinheiten, sprich Taktzylen, müssen wir nun einfach nur einen bestimmten Grund- wert in beide Timer laden, sie starten und anschließend das zu prüfende Pro- gramm aufrufen. Dies wollen wir mittels eines "JSR"-Befehls tun. Springt das aufgerufene Programm nun zurück, so müs- sen 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, zwi- schen 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 aufgeli- stet, welche Möglichkeiten es hier gibt. Timer B kann nämlich in Gegensatz zu Timer A vier (anstelle von zweien) ver- schiedene Triggerquellen haben. Dies wird von den Bits 5 und 6 gesteuert, deren Kombinationen ich Ihnen noch ein- mal 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. Dem- nach 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 folgender- maß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 Ganzzah- lanteil des Ergebnisses als höherwer- tiges Word. Dieses wird nun wie ge- wohnt in Low- und Highbyte aufgespal- ten. 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 niederwer- tige Word, das ebenfalls, wie ge- wohnt, in Low- und Highbyte umgewan- delt wird.