Taktzyklenzähler: Aufs Timing kommt es an

Taktzyklentabellen sind ungenau - das haben viele Tests bewiesen. TOS präsentiert ein Programm, mit dem Sie die Laufzeit Ihrer Routinen exakt messen können.

Programmierer benötigen die exakte Taktzyklenzahl einer Assembler-Routine, wenn die Routine beispielsweise zeitlich genau abgestimmt sein soll - wie etwa bei komplizierter Rasterinterrupt-Programmierung. Oder wenn die Routine besonders geschwindigkeits-optimiert sein soll, wie es beim Sound-Sampling und für 3D-Grafik notwendig ist.

Zwar verraten auch Taktzyklen-Tabellen die Ausführungszeit von Maschinenbefehlen. Aber alle mir bekannten Tabellen enthalten falschen Angaben, die nur all zu oft zu unliebsamen Überraschungen führen. Davon abgesehen ist das Errechnen der Taktzyklen längerer Programme sehr aufwendig. Daher bietet Ihnen TOS ein Taktzyklenmessprogramm, das diese unliebsame Arbeit abnimmt.

Leider reicht es beim ST manchmal nicht, die Anzahl der Taktzyklen anhand einer Tabelle auszurechnen. Der 68000-Prozessor greift nämlich nur alle vier Taktzyklen auf den Speicher zu. Er teilt die Bus-Zugriffe mit dem für den Bildschirmaufbau zuständigen Video-Shifter. Dieser arbeitet mit einem Viertel der Taktfrequenz des Prozessors (2 MHz). Deshalb verstreichen manchmal - abhängig von der Anordnung der Befehle - zwei Taktzyklen ungenutzt, bis der Prozessor wieder auf den Speicher zugreifen kann.

Ein Beispiel: Wenn auf den Befehl »exg.l Dn,Dn«, der sechs Taktzyklen benötigt, ein »nop« folgt, so wartet der Prozessor erst zwei Taktzyklen, bis er mit der Abarbeitung des »nop«-Befehls beginnt. Sie sehen das Problem? Um die Wartezyklen zwischen zwei Maschinenbefehlen besser zu erkennen, arbeitet unser Programm mit zwei Angaben, dem End- und dem Startwert. Der Endwert gibt an, ob der erste der beiden Befehle ein Vielfaches von vier Taktzyklen benötigt (Wert 2) oder nicht (Wert 0). Der Startwert gibt das gleiche für den darauffolgenden Befehl an.

So hat »exg.l Dn,Dn« (sechs Taktzyklen) in unserem Beispiel den Endwert 2 und »nop« (vier Taktzyklen) den Startwert 0. Das heißt, daß »nop« immer nur alle vier Taktzyklen beginnen (Startwert 0) kann, während die »exg.l Dn,Dn«-Anweisung nicht auf ein Vielfaches von 4 endet (Endwert 2). Allgemein gesagt: Immer, wenn auf einen Befehl ein weiterer folgt, dessen Startwert ungleich dem Endwert des ersten ist, wartet der Prozessor zwei Taktzyklen, bevor er ihn abarbeitet.

Deshalb zeigt das Programm »Zyklos« den Anfangswert des ersten Befehls, den Endwert des letzten Befehls und die gesamte Taklzyklendauer der zu testenden Routine an. Aus programmtechnischen Gründen darf Ihre Routine im monochromen Modus nicht länger als ca. 1/90 s und im 50 Hz-Farbmodus nicht länger als ca. 1/80 s laufen. Da diese Zeitspanne manchmal nicht ausreicht, darf in der erweiterten Version namens »x__Zyklos« jede Routine im monochromen Modus etwa 1,9 s und in Farbe etwa 4 s dauern.

x__Zyklos mißt allerdings Start- und End-Wert nicht, da bei einer so langen Zeit zwei bzw. vier zusätzliche Taktzyklen unerheblich sind.

Die Funktionsweise von Zyklos

Wer Zyklos und x__Zyklos nur anwenden möchte, kann die folgenden Abschnitte überspringen, denn jetzt erklären wir die Funktionsweise der Programme. Als »Messgerät« dient nicht, wie vielleicht zu erwarten wäre, ein MFP-Timer. Denn diese haben leider eine sehr krumme Taktfrequenz von 2,4576 MHz. Außerdem bedeutet die Verwendung eines MFP-Timers, daß die Routine mehrmals hintereinander ablaufen muß, um einen genauen Meßwert zu erzielen. Daher lassen sich damit nur kurze Routinen messen. Stattdessen benutzt Zyklos den Videoadressen-Zähler, der sich alle vier Taktzyklen um 2 erhöht.

Zunächst sorgt Zyklos dafür, daß der Prozessor wartet, bis der Rasterstrahl mit dem Aufbau der ersten Videozeile beginnt. Damit hat das Programm einen festen Bezugspunkt. So muß Zyklos später nur noch den Videoadressen-Zähler lesen, die Differenz zum Anfangspunkt berechnen und das Ergebnis mit 2 multiplizieren. Problematisch dabei: Der Videoadressen-Zähler besteht aus drei einzelnen Byte-Registern (Low/Mid/High). Zyklos kann also nicht den ganzen Wert auf einmal lesen. Nach etwas Probieren fand sich die Lösung: Zyklos muß zuerst warten, bis das Low-Register auf Null ist. Dann erst liest es das Mid-Register.

Wenn der Wert des Mid-Registers nicht seinem Anfangswert entspricht, wiederholt Zyklos den Meßvorgang. Andernfalls geht es davon aus, daß entweder gerade der Aufbau der ersten Bildschirmzeile begonnen hat, oder der Rasterstrahl gerade im oberen Randbereich herumgeistert. Im letzteren Fall ändert der Videoadressen-Zähler seinen Wert solange nicht, bis der Rasterstrahl beginnt, die erste Bildschirmzeile aufzubauen. Deshalb wartet Zyklos solange, bis sich das Low-Register ändert.

Nun weiß das Programm aber lediglich, daß sich der Rasterstrahl irgendwo am Anfang der ersten Videozeile befindet. Allerdings soll die Meßroutine immer an genau derselben Stelle beginnen. Dazu zieht Zyklos den zuletzt gelesenen Low-Wert von einem festen Wert ab. Dieser feste Wert muß so groß sein, daß in jedem Fall nach der Subtraktion das Ergebnis größer oder gleich Null ist. Anschließend hält ein »rol.l Dn,Dn« den Computer ein wenig auf.

Der »rol«-Befehl benötigt genau 8+2*[Wert der untersten 6 Bits des Quelloperanden (0 bis 63)] Taktzyklen. Der eingelesene Wert des Low-Registers entspricht genau der Hälfte der seit Anfang der ersten Zeile verstrichenen Taktzyklen. Die geeignete Wahl des Quelloperanden beim »rol«-Befehl stellt sicher, daß sich der Rasterstrahl immer an derselben Stelle befindet. Denn die Taktzyklen, die das Programm am Anfang »nachhinkte«, werden durch den »rol«-Befehl eingespart. Nun folgt das nächste Problem: die seitlichen Ränder. Denn solange der Computer den Randbereich aufbaut, bleibt der Videoadressen-Zähler stehen. Im monochromen Betrieb benötigt der Randbereich einer Zeile exakt 64 Taktzyklen. In Farbe und bei 50 Hz Bildschirmfrequenz sind es sogar 192 Taktzyklen. Der meßbare Bereich beträgt dagegen in Monochrom 160 Taktzyklen und in Farbe 320 Taktzyklen. Pro Zeile einschließlich Rand benötigt der Computer in Monochrom also insgesamt 224 Taktzyklen, in Farbe verbraucht er 512.

Im monochromen Modus erhöht sich der Videoadressen-Zähler dabei um 160/2=80 Byte, in Farbe um 160 Byte. Zyklos dividiert daher die gemessene Adress-Differenz durch 80 bzw. 160. Denn dadurch erhält es die Anzahl der vollständig durchlaufenen Videozeilen. Das Ergebnis multipliziert Zyklos ohne Rest mit 224 bzw. 512. Der Rest der Division entspricht genau der Hälfte der Taktzyklen, die in der zuletzt angefangenen Zeile verbraucht wurden. Diesen Rest multipliziert Zyklos mit 2 und addiert ihn zum Ergebnis. Schon hat es die gesamte Taktzyklendauer der Routine errechnet. Aber was tun, wenn sich der Computer bei der Messung gerade im Randbereich befand? Das würde bedeuten, daß der errechnete Wert um bis zu 64 und in Farbe um 192 Taktzyklen vom wirklichen Wert abweicht. Denn der Zähler verändert sich in diesem Bereich nicht und »täuscht« uns laufend vor, schon am Anfang der nächsten Zeile zu sein.

# Programminformation

Name: Zyklos, xZyklos
Sprache: Assembler (GFA-Assembler 1.3)
Programmautor: Günther Donderer
Farbe: ja
Monochrom: ja
Beschreibung: Zyklos mißt die exakte Laufzeit einer Assembler-Routine in Taktzyklen

War die Messung genau genug?

Dieses Problem umgeht Zyklos, indem es zweimal den Low- und Mid-Wert ausliest - aber mit 64 bzw. 192 Taktzyklen Abstand, was der Länge des Randes entspricht. Später testet es, ob die erste Messung im Randbereich stattfand. Das erfährt Zyklos durch das Ergebnis der vorhin durch geführten Division der Differenz durch 80 bzw. 160. Wenn kein Rest übrig blieb, war die Messung entweder genau am Anfang einer Videozeile oder im Randbereich. In diesem Fall nimmt Zyklos die zweite Messung, die wegen der 64 bzw. 192 Taktzyklen Abstand nicht mehr im Randbereich gewesen sein kann. Am Ende zieht das Programm die zusätzlich abgewarteten 64 bzw. 192 Taktzyklen ab.

Nun kann man aber Mid- und Low-Wert nicht gleichzeitig lesen. Wenn man etwa zuerst den Low-Wert liest, kommt es vor, daß sich der anschließend gelesene Mid-Wert zwischen den beiden Lesevorgängen erhöht hat. Zyklos liest zuerst einen Low-Wert, dann einen Mid-Wert, wartet 64 Taktzyklen und liest wieder einen Low- und einen Mid-Wert.

Da die Taktzyklendifferenz zwischen dem Lesen des Low- und des Mid-Wertes je acht Taktzyklen beträgt (Messung mit Move.b (Ax),Dx), testet Zyklos bei der ersten Messung, ob der Low-Wert größer 256-(8/2) ist. Wenn ja, dann geht es davon aus, daß der Mid-Wert um eins größer ist, als er zum Zeitpunkt der Messung des Low-Wertes war. In diesem Fall erniedrigt Zyklos den Mid-Wert um eins.

Start- und Endwert bestimmen

Hat die Messung im Randbereich stattgefunden, so ist der Low-Wert immer ein Vielfaches von 16. Er liegt also nie im Bereich zwischen 252 und 256. Somit tritt kein Überlauffehler beim Vergleich mit 256-(8/2) auf. Nun testet Zyklos, ob der erste Meßwert im Randbereich war. In dies der Fall, so unterzieht Zyklos die zweite Messung derselben Behandlung wie die erste. Dabei merkt es sich, daß es später vom Ergebnis die zusätzlich abgewarteten 64 bzw. 192 Taktzyklen wieder abziehen muß.

Somit hat Zyklos endlich die Taktzyklendauer der Routinen festgestellt. Jetzt muß das Programm nur noch den Start- und den End-Wert herausfinden. Dazu ist es notwendig, drei verschiedene Messungen durchzuführen. Beim ersten Mal bindet Zyklos die zu testende Routine zwischen zwei »nop«-Befehle (Dauer = 4 Taktzyklen, Startwert = 0, End wert = 0) ein, beim zweiten Mal zwischen einen »exg Dn,Dn«-Befehl (Dauer = 6 Taktzyklen, Startwert = 0, End-wert = 2) und einen »nop«. Beim dritten Mal liegt die Routine zwischen einem »nop« und einem »bra.s« mit Verzweigung (Dauer = 10 Taktzyklen, Startwert = 2, Endwert = 0).

Den Startwert 2 einer Routine erkennt Zyklos daran, daß die zweite Messung ebenso lang ist wie die erste. Denn ansonsten wäre sie vier Taktzyklen länger. Den Endwert 2 erkennt Zyklos daran, daß die dritte Messung acht Taktzyklen länger ist als die erste. Denn ansonsten wäre sic zwölf Taktzyklen länger. Die Taktzyklenlänge errechnet Zyklos, indem es von der ersten Messung sowohl Start- als auch End-Wert der Routine abzieht. Denn hierbei hat die Routine immer zwei Taktzyklen gewartet, wenn der Start- bzw. End-Wert 2 war. So, das war alles zur Programmversion Zyklos. Da die Programmversion zum Messen längerer Routinen (x__Zyklos) den Start-und End-Wert nicht feststellt, führt sie nur eine Messung durch. Allerdings ergeben sich zwei neue Probleme: Die zu testende Routine kann irgendwo im unteren bzw. oberen Randbereich enden oder länger als einen Bildschirmaufbau dauern. Die Anzahl der durchlaufenen Bildschirme testet x__Zyklos mit dem MFP-Timer, indem es ihn als Event-Zähler verwendet. Das bedeutet, daß es die Anzahl der durchlaufenen Zeilen mißt. Nun hat der Zähler allerdings nur ein Byte-Register, und allein ein Bildschirmaufbau entspricht in Monochrom 400 Zeilen.

Da hilft wieder ein Trick: x_Zyklos stellt den Zählwert am Anfang der ersten Zeile auf 133. Nach der Messung der verbrauchten Taktzyklen innerhalb des letzten Bildschirms wartet x_Zyklos dann, bis der Rasterstrahl wieder mit der ersten Zeile anfängt. Dann mißt es den aktuellen Timer-Wert. Dieser beträgt am Anfang des zweiten Bildschirms 132 und vermindert sich pro zusätzlich durchlaufenen Bildschirm um eins.

So ermittelt x_Zyklos mit geringem Rechenaufwand die Anzahl der durchlaufenen Bildschirme. Nun ist nur noch das Problem mit dem unteren bzw. oberen Rand zu lösen, x__Zyklos erkennt, daß die Messung in diesem Bereich stattfand, wenn beide Messwerte im Rand liegen, also Vielfache von 80 sind. Nun wartet es solange, bis es sich sicher nicht mehr im Rand befindet. Das entspricht in Monochrom etwa der Zeit von 101 Zeilen (22624 Taktzyklen).

Nun führt x-Zyklos die Messungen erneut durch. Die zusätzlich abgewarteten Taktzyklen zieht es vom Endergebnis ab, und schon ist das Problem beseitigt. In Farbe läuft das ganze fast genauso, nur mit anderen Werten ab.

Einfach zu bedienen

Jetzt aber endlich zur Bedienung der Programme: Im Listing befindet sich das Label »Regs«. Dort tragen Sie ein, welche Werte die Register D0-D7 und A0-A6 bei Beginn des Programms annehmen sollen. Weiterhin gibt es in Zyklos eine mit »zu testendes Programm« bezeichnete Stelle. Dort fügen Sie Ihre zu testende Routine ein. Bei der Version für längere Programme (x__Zyklos) wird die Routine als Datei mit dem Namen »PROG.IS« per »Include«-Direktive eingebunden. Sollten Sie einen Assembler besitzen, der nicht über eine Include-Anweisung verfügt, hängen Sie die Routine vor den Programmtext und entfernen den Include Befehl.

Beachten Sie, daß die Version für kurze Programme (Zyklos) die Routine dreimal durchläuft. Dies führt bei Programmen, die sich selbst verändern, zu Problemen. Denken Sie daran, daß x__Zyklos für die Messung den Timer B verwendet und Sie diesen nicht ändern sollten. Nach Start des assemblierten Textes geben unsere Taktzyklenzähler die Meßwerte aus. (ba)


Günther Donderer
Aus: TOS 05 / 1990, Seite 79

Links

Copyright-Bestimmungen: siehe Über diese Seite