Im Magazin „Dr. Dobb's Journal of Computer & Orthodontia" wurde in der Nummer 55, Mai 1981, der folgende Artikel (hier in deutscher Übersetzung) abgedruckt.
|
Gebrauch (und Missbrauch) des Z-80 Mikroprozessors
von Ray Duncan
Ray Duncan, Laboratory Microsystems, 4147 Beethoven Street, Los Angeles, CA 90066.
Seit seiner Einführung umgibt den Zilog Z-80 Mikroprozessor eine Aura des Zaubers und der Stärke verglichen mit dem Arbeitspferd Intel 8080 oder 8085.
Der gute Ruf begründet sich auf einige Erweiterungen von zusätzlichen Instruktionen,
Registern, Adressierungsmethoden und der Vektorinterruptfähigkeit der Z-80.
Werden jedoch die zusätzlichen Z80-Instruktionen nicht sehr sorgfältig angewendet, dann können diese einen deutlichen Rückgang im Durchsatz bewirken verglichen mit der Nutzung der 8080 Instruktionen allein (natürlich ist gleiche Taktfrequenz vorausgesetzt).
Insbesondere der unüberlegte Gebrauch der Indexregister kann zu schweren Einbußen bezogen auf Geschwindigkeit und Speicherbedarf führen.
In diesem kurzen Artikel will ich versuchen, einige "gute" und "schlechte" Beispiele bei der Z-80 Programmierung aufzeigen.
Es wird durchweg die Zilog-Schreibweise der Übersichtlichkeit wegen benutzt.
Alle Ausführungszeiten beziehen sich auf einen 4 MHz Systemtakt und sind den von Zilog Inc. veröffentlichten Spezifikationen entnommen.
Man betrachte das folgende einfache Beispiel eines "indirekten" 16-Bit Ladevorgangs aus dem Speicher in das Register BC, wobei als Vorgabeadresse der Inhalt einer genannten Variablen verwendet wird:
8080 Version | Bytes | µsek |
LD HL,(Variable) | 3 | 4.00 |
LD C,(HL) | 1 | 1.75 |
INC HL | 1 | 1.50 |
LD B,(HL) | 1 | 1.75 |
| 6 | 9.00 |
|
Z-80 Version | Bytes | µsek |
LD IX,(Variable) | 4 | 5.00 |
LD C,(IX) | 3 | 4.75 |
LD B,(IX+1) | 3 | 4.75 |
| 10 | 14.50 |
Die auf die Z-80 ausgerichteten Instruktionen sehen viel übersichtlicher aus, allerdings benötigen sie etwa 60% mehr sowohl für die Ausführungszeit als auch den Speicherbedarf.
Wie sieht es mit der indirekten Erhöhung eines Zählers im Speicher aus?
8080 Version | Bytes | µsek |
INC (HL) | 1 | 2.75 |
|
Z-80 Version | Bytes | µsek |
INC (IX) oder (IY) | 3 | 5.75 |
Der Gebrauch der Indexregister in diesem Fall führt zu mehr als einer Verdoppelung von Ausführungszeit und Speicherbedarf!
Der Einsatz von Indexregistern kann auch von Nachteil sein, wenn diese in Unterprogrammen gespeichert und wieder geladen werden müssen:
8080 Version | Bytes | µsek |
PUSH BC oder DE oder HL | 1 | 2.75 |
POP BC oder DE oder HL | 1 | 2.50 |
|
Z-80 Version | Bytes | µsek |
PUSH (IX) oder (IY) | 2 | 3.75 |
POP (IX) oder (IY) | 2 | 3.50 |
Man würde sich vorstellen, dass die Indexregister gut geeignet seien für Tabellenumsetzungen mittels Erhöhen des Indexes zum Vergleich von Bytes in einer Tabelle, bis eine Übereinstimmung erfolgt ist.
Danach Anwendung des Adressierungsmodus indizierter Offset, um aus einer anderen Tabelle das entsprechende Byte zu entnehmen.
Sogar in den einfachereren Fällen überwiegt wieder der Überhang der Indexregister die sauberen Assembleranweisungen.
Zum Beispiel:
8080 Version | Bytes | µsek |
CP A,(HL) | 1 | 1.75 |
JP NZ,Ungleich | 3 | 2.50 |
LD DE,Offset | 3 | 2.50 |
ADD HL,DE | 1 | 2.75 |
LD A,(HL) | 1 | 1.75 |
| 9 | 11.75 |
|
Z-80 Version | Bytes | µsek |
CP A,(IX) | 3 | 4.75 |
JP NZ,Ungleich | 3 | 2.50 |
LD A,(IX+Offset) | 3 | 4.75 |
| 9 | 12.00 |
Was ist mit den relativen Sprüngen (JR)?
Viele Programmierer, die den Z-80 gerade neu entdecken, bemerken, dass die unbedingten JR Instruktionen nur zwei Bytes belegen verglichen mit den drei Bytes, benötigt von den absoluten Sprunganweisungen (JP).
Und schnell werden mit dem Editor all die knappen zeitabhängigen Schleifen bearbeitet, um diese gepflegten Instruktionen einzubauen.
Überraschung - die Instruktionen JR benötigt 3 µSekunden für die Ausführung verglichen mit 2,5 µSekunden für die Instruktion JP, eine Erhöhung um ca. 20% in der Ausführungszeit.
Nun, da es ausgezeichnete Assembler für verschiebbaren Kode gibt, sollte der Gebrauch von unbedingten JR-Instruktion möglichst vermieden werden.
Andererseits gibt es einen leichten Vorteil bei der Ausführungszeit von bedingten relativen Sprüngen (1,75 µSekunden bei nicht eingetroffener Bedingung und 3,00 µSekunden bei eingetroffener Bedingung) gegenüber den bedingten absoluten Sprüngen (immer 2,5 µSekunden), so lange der Sprung nicht ausgeführt wird in mehr als 50% der Zeit.
Selbstverständlich wäre der Z-80 nicht so populär geworden, wenn da nicht erhebliche Pluspunkte bei der Hard- und Software für seinen Einsatz wären.
Das sorgfältige Studium des Instruktionssatzes gibt eine Anzahl von Fällen preis, bei denen der Einsatz der Z-80 Instruktionen und Register beachtliche Verbesserungen bei der Ausführungszeit und dem Speicherbedarf bringen kann.
Man betrachte den Aufruf eines Unterprogramms, in dem alle Register für Zwischenspeicherungen benötigt werden, jedoch verlangt das Hauptprogramm, dass am Ende des Aufrufs alle Registerinhalte unverändert bleiben.
Die 8080 verlangt, dass alles auf dem Stack abgelegt und später zurückgeholt wird.
Aber die Z-80 erlaubt den Wechsel mit den alternativen Registern, was zu einem enormen Zeitgewinn führt:
8080 Version | Bytes | µsek |
PUSH BC | 1 | 2.75 |
PUSH DE | 1 | 2.75 |
PUSH HL | 1 | 2.75 |
(Unterprogrammteil) |
POP HL | 1 | 2.50 |
POP DE | 1 | 2.50 |
POP BC | 1 | 2.50 |
| 6 | 15.75 |
|
Z-80 Version | Bytes | µsek |
EXX | 1 | 1.00 |
(Unterprogrammteil) |
EXX | 1 | 1.00 |
| 2 | 2.00 |
Die erweiterten Fähigkeiten der 16-Bit Arithmetik der Z-80 sind ein Glücksfall sowohl aus Gründen des Durchsatzes als auch für die Übersichtlichkeit der Programm-Kodierung.
Als Beispiel die Subtraktion DE von HL, mit dem Ergebnis in HL:
8080 Version | Bytes | µsek |
LD A,L | 1 | 1.00 |
SUB A,E | 1 | 1.00 |
LD L,A | 1 | 1.00 |
LD A,H | 1 | 1.00 |
SBC A,D | 1 | 1.00 |
LD H,A | 1 | 1.00 |
| 6 | 6.00 |
|
Z-80 Version | Bytes | µsek |
SBC HL,DE | 2 | 3.75 |
(Die setzt voraus, dass der Zustand des Carry-Flags bekannt ist, andernfalls muss dieses ausgeschaltet werden mit der vorangestellten Instruktion OR A,A
, die etwas Zeit kostet.)
Die zusätzlichen Instruktionen zum direkten Laden und Speichern der Register sind bei dem Z-80 sowohl handlich als auch schnell.
Wir vergleichen die Aufgabe, einen 16-Bit Wert aus dem Speicher in das Register BC zu laden:
8080 Version | Bytes | µsek |
PUSH HL | 1 | 2.75 |
LD HL,(Adresse) | 3 | 4.00 |
LD B,H | 1 | 1.00 |
LD C,L | 1 | 1.00 |
POP HL | 1 | 2.50 |
| 7 | 11.25 |
|
Z-80 Version | Bytes | µsek |
LD BC,(Adresse) | 4 | 5.00 |
Der Z-80 verfügt über eine Verschiebe-Instruktion für Zeichenketten, die, wie allgemein bekannt, so gut wie die Kosten für den Chip selber Wert ist.
Wenn man in das Register HL die Quell-, in DE die Zieladresse und in BC die Anzahl Bytes,
die verschoben werden sollen, einsetzt:
8080 Version | Bytes | µsek |
SCHLEIFE: | LD A,(HL) | 1 | 1.75 |
| LD (DE),A | 1 | 1.75 |
| INC HL | 1 | 1.50 |
| INC DE | 1 | 1.50 |
| DEC BC | 1 | 1.50 |
| LD A,B | 1 | 1.00 |
| OR A,C | 1 | 1.00 |
| JP NZ,SCHLEIFE | 3 | 2.50 |
| 10 | 12.50 pro Schleife |
|
Z-80 Version | Bytes | µsek |
LDIR | 2 | 5.25 für jedes übertragene Byte |
Der entsprechende Vergleich mit Auto-Inkrement Instruktionen ist schwieriger im wirksamen Einsatz, sie können aber mit guten Ergebnissen in Routinen zur Tabellenverarbeitung eingesetzt werden.
Das Anliegen dieser kleinen Veranschaulichung war, dass der Z-80 in der Tat ein leistungsfähiger Prozessor ist, aber der sachgerechte Einsatz seiner vielen Zusatzfähigkeiten in zeitabhängigen Anwendundungen bedarf einer sehr guten Kenntnis seiner Instruktionen auf Maschinenebene.
Die sehr eleganten Zilog Mnemonics, die die Verständlichkeit eines Assemblerausdrucks gewaltig verbessern veglichen mit denen von Intel, verwischen den Unterschied der Kodierung auf Maschinebene für einen neuen Programmierer, da sie alle Register-Speicher und Register-Register Operationen ähnlich aussehen lassen.
Ich hoffe, dass andere Leser angeregt werden, andere Beispiele für "gute" und "schlechte"
Nutzung des Z-80 beizusteuern.
Übersetzt von
Werner Cirsovius
September 2002
© Dr. Dobb's Journal