ASM86FAQ - Basiswissen
Was ist Maschinensprache?
Juergen Thelen 2:2450/55.5:
Maschinensprache ist von der Programmiererseite aus die tiefste Ebene, auf der mit einem Prozessor kommuniziert werden kann.
Die Kommunikation erfolgt anhand binär kodierter Befehle (Opcodes), die auf auf elektrischem/elektronischem Weg an den Prozessor übermittelt werden.
Der Prozessor dekodiert den Binärbefehl und führt die entsprechende, vom Prozessorhersteller festgelegte, Aktion aus.
Beispiel:
Erreicht einen Prozessor der Intel x86 Baureihe der Opcode f8H (11111000b), wird der Prozessor daraufhin das Carry-Flag seines Flaggenregisters löschen.
Maschinensprache ist die einzige Sprache, die ein Prozessor überhaupt direkt verstehen kann. Alle anderen Sprachen, wie z.B. Basic, Pascal oder C, lassen den Programmierer zwar Quelltexte in einem weitaus angenehmeren Format er- stellen, müssen jeden Quelltext jedoch immer erst zurück in Maschinensprache übersetzen, damit ein Prozessor sie verarbeiten bzw. ausführen kann.
Was ist Assembler?
Juergen Thelen 2:2450/55.5:
Bekanntlich verstehen Prozessoren nur Maschinensprache. Maschinensprache besteht nur aus Opcodes, also Bytes oder Bytefolgen, deren binäre Kodierung eine festgelegte Aktion im Prozessor auslöst.
Allerdings existieren nun, je nach Prozessor, Hunderte verschiedene solcher Opcodes. Diese Opcodes alle auswendig kennen bzw. Opcode-Tabellen wälzen zu müssen, um überhaupt programmieren zu können, ist natürlich nicht gerade als angenehme Form der Programmierung zu bezeichnen.
Die Sprache Assembler beseitigt diesen Nachteil, indem sie dem Programmierer ermöglicht, anstelle von Opcodes kurze, einprägsame Worte zu benutzen. Diese Worte werden als Mnemonics bezeichnet.
Beispiel:
Opcode Mnemonic
f8H (11111000b) CLC (für CLear Carry)
Um ein Programm in Assembler entwickeln zu können, benötigt man nun einen Assembler. Das klingt paradox, aber mit letzterem Assembler ist ein Programm gemeint, daß einen in Assembler (der Sprache) geschriebenen Quelltext wieder zurück in Maschinensprache (Opcodes) übersetzen kann.
Hochsprachenanwendern bietet sich diese Konfusion nicht, dort nennt man die Rückübersetzung eines Quelltextes Kompilieren, es sei denn, es handelt sich um eine interpretierende Hochsprache (Rückübersetzung während der Laufzeit).
Warum Assembler?
Juergen Thelen 2:2450/55.5:
Ich persönlich verwende Assembler immer dann, wenn sich etwas mit einer Hochsprache entweder gar nicht, oder nur zu inaktzeptablen Bedingungen rea- lisieren lässt. Das trifft in folgenden Situationen zu:
1. Ich brauche mehr Speed (nein, keine Aufputscher ;)). Hochsprachen sind dazu verdammt, Quelltexte nach einem festgelegten Schema zu übersetzen. Dabei entsteht selten optimaler Code. Oft werden Register mit Werten aus Speicherzellen geladen, die bereits im Zielregister oder einem anderen Register vorhanden wären, Register gelöscht, die bereits gelöscht sind, unnütze Stackorgien gefeiert, usw.
Von Code und Data Alignment, unrolled loops und Prefetchqueue-Abstimmung reden wir besser erst gar nicht... <g>
Wie groß der Geschwindigkeitsgewinn in Assembler ist, ist natürlich ab- hängig davon, welches Problem gelöst wird. Faktoren zwischen doppelt bis zehnfach, manchmal sogar hundertfach, sind aber meist realistisch.
Speziell im Bereich zeitkritischer Programmierung steht man bei Einsatz von Hochsprachen vor einem Berg von Problemen, die ohne Assembler kaum, manchmal sogar überhaupt nicht, zu lösen wären.
2. Ich brauche direkten Zugriff auf Prozessor- oder System-Interna, der von der Hochsprache nicht, oder nur auf Umwegen, unterstützt wird. Speziell bei Operationen im Protected Mode (z.B. Descriptortables, CPL, IOPL), dem Switchen zwischen Real und Protected Mode (Thunking), dem Realisieren von RealMode/Flat4G usw. steht man mit Hochsprachen meist im Regen.
3. Ich möchte mich aus einer Speichermangel-Situation befreien. Besonders bei der Programmierung von Shells, TSRs und Device-Treibern kann einen der unoptimierte Code einer Hochsprache in den Wahnsinn treiben... ;)
Zahlensysteme
Juergen Thelen 2:2450/55.5:
Im Bereich der Mathematik herrschen meist Einheiten des Zehnersystems, seien es DM, Liter, Kilogramm, oder was auch immer. Das ist bei der Programmierung in Assembler etwas anders. Dort wird überwiegend im dualen (binären, 2) und hexadezimalen (16) System "gedacht".
Es existiert aber kein großer Unterschied zwischen diesen Zahlensystemen, es werden lediglich die Stellen als Potenzen anderer Basis interpretiert:
2er 10er 16er
1. Stelle 1 (2^0) 1 (10^0) 1 (16^0) 2. Stelle 2 (2^1) 10 (10^1) 16 (16^1) 3. Stelle 4 (2^2) 100 (10^2) 256 (16^2) 4. Stelle 8 (2^3) 1000 (10^3) 4096 (16^3) 5. Stelle 16 (2^4) 10000 (10^4) 65536 (16^4) 6. Stelle 32 (2^5) 100000 (10^5) 1048576 (16^5) 7. Stelle 64 (2^6) 1000000 (10^6) 16777216 (16^6) 8. Stelle 128 (2^7) 10000000 (10^7) 268435456 (16^7)
Vom Zehnersystem ist jedem bekannt, daß jede Stelle von 0 bis 9 durchgezählt werden kann, bevor eine weitere Stelle (10) erforderlich wird.
Diese Logik gilt auch für die beiden anderen Systeme. Auch hier wird jede Stelle von 0 an durchgezählt, nur der Grenzwert, der eine weitere Stelle erforderlich macht, ist hier nicht mehr 9, wie im Zehnersystem, sondern eben 1 (Zweiersystem) bzw. 15 (Sechszehnersystem).
Im 16er-System wirft sich allerdings eine Frage auf: Daß jede Stelle von 0 bis 15 durchgezählt wird, ist ja nachvollziehbar, aber wie sollen 10 bis 15 in einer Stelle dargestellt werden? Die Lösung ist simpel: Die zweistelligen Zahlen 10 bis 15 werden durch die einstelligen Buchstaben A bis F ersetzt.
Zur Verdeutlichung eine Addition (7+12) einmal in allen drei Systemen:
2er 10er 16er
111 = 7 = 7 + 1100 = + 12 = + c ------ ---- --- 10011 = 19 = 13
Die Probe:
2er (bin): 10011b = dez (1*16) + (0*8) + (0*4) + (1*2) + (1*1) = 19 10er (dez): 19 = dez (1*10) + (9*1) = 19 16er (hex): 13h = dez (1*16) + (3*1) = 19
Das binäre und hexadezimale System sind zu Beginn zwar etwas gewöhnungsbe- dürftig, da sich im Unterbewußtsein immer wieder das dezimale System nach vorne drängelt, aber mit der Zeit wird es zunehmend einfacher... ;)
Datengrößen
Juergen Thelen 2:2450/55.5:
Der physikalische Arbeitsspeicher (RAM) eines Rechners ist als aufsteigend adressierte Folge von Bytes organisiert:
Adresse Speicherzelle
: : 3 + Byte + +++++++++++++ 2 + Byte + +++++++++++++ 1 + Byte + +++++++++++++ 0 + Byte + +++++++++++++
Bei der Programmierung in Assembler wird hauptsächlich mit den Datengrößen Bit, Byte, Word und Doubleword gearbeitet.
Ein Byte besteht aus 8 aufeinanderfolgenden Bits und kann sich an jeder be- liebigen Adresse des Arbeitsspeichers befinden. Die Bits sind von 0 bis 7 durchnummeriert, wobei Bit 0 als Least Significant Bit (LSB, niederwertig- stes Bit) und Bit 7 als Most Significant Bit (MSB, höchstwertiges Bit) be- zeichnet wird:
+---- Byte -----+
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| +-+-+-+-+-+-+-+-+
Ein Word besteht aus 2 aufeinanderfolgenden Bytes und kann an jeder belie- bigen Byteadresse beginnen. Die Bits sind von 0 (LSB) bis 15 (MSB) durchnum- meriert. Bits 0 bis 7 werden dabei als LowByte, Bits 8 bis 15 als HighByte bezeichnet:
+------------- Word ------------+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+--- HighByte --+--- LowByte ---+
Allerdings ist die Ablagereihenfolge der beiden Bytes im Speicher nicht so, wie man es vielleicht erwartet: Words werden auf Prozessoren der Intel 8086/ Pentium Families (& Kompatiblen) im Little-Endian Format gespeichert, d.h. es wird zuerst das Low- und dann das Highbyte abgelegt. Als Beispiel ein Word mit dem Wert 4321h (LowByte 21h, HighByte 43h):
Adresse Speicherzelle
: :
+-+-+-+-+-+-+-+-+ --+ |f|e|d|c|b|a|9|8| | 1 --- | | | | 43h | | +-+-+-+-+-+-+-+-+ +-- Word (Little-Endian) |7|6|5|4|3|2|1|0| | 0 --- | | | | 21h | | +---------------+ --+
Ein Doubleword besteht aus 2 aufeinanderfolgenden Words und kann ebenfalls an jeder beliebigen Byteadresse beginnen. Die Bits sind hier von 0 (LSB) bis 31 (MSB) durchnummeriert. Bits 0 bis 15 werden als LowWord, Bits 16 bis 31 als HighWord bezeichnet:
+--------------------------- Doubleword ------------------------+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1| | | | | | | | | | | | | | | | | |f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0|f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+----------- HighWord ----------+----------- LowWord -----------+
+-- HighByte ---+--- LowByte ---+-- HighByte ---+--- LowByte ---+
Auch Doublewords werden im Little-Endian Format abgelegt, zuerst LowWord, dann HighWord. Die beiden Words entsprechend: zuerst LowByte, dann HighByte. Als Beispiel ein Doubleword mit dem Wert 87654321h:
Adresse Speicherzelle
: :
+-+-+-+-+-+-+-+-+ --+ |1|1|1|1|1|1|1|1| | |f|e|d|c|b|a|9|8| | 3 --- | | | | 87h | | +-+-+-+-+-+-+-+-+ | |1|1|1|1|1|1|1|1| | |7|6|5|4|3|2|1|0| | 2 --- | | | | 65h | | +-+-+-+-+-+-+-+-+ +-- Doubleword (Little-Endian) |f|e|d|c|b|a|9|8| | 1 --- | | | | 43h | | +-+-+-+-+-+-+-+-+ | |7|6|5|4|3|2|1|0| | 0 --- | | | | 21h | | +---------------+ --+
Datentypen
Juergen Thelen 2:2450/55.5:
Die Prozessoren der Intel 8086 / Pentium Families (& Kompatiblen) interpre- tieren Operanden in Abhängigkeit der zugehörigen Instruktionen als unter- schiedliche Datentypen:
UNSIGNED (oder Ordinal) -----------------------
Darunter versteht man positive Ganzzahlen, bei denen die gesamte Daten- breite direkt als Wert interpretiert wird:
Byte 8 Bit, Wertebereich 0 .. 255 Word 16 Bit, Wertebereich 0 .. 65.535 DWord 32 Bit, Wertebereich 0 .. 4.294.967.295
SIGNED (oder Integer) ---------------------
Darunter versteht man positive und negative Ganzzahlen, bei denen der Wert im 2er Komplement vorliegen muss. Im 2er Komplement dient das MSB als Vor- zeichenbit (0 = positiv, 1 = negativ), d.h. die tatsächliche Datenbreite schrumpft um ein Bit:
Shortint 8 Bit, Wertebereich -128 .. 127 Integer 16 Bit, Wertebereich -32.768 .. 32.767 Longint 32 Bit, Wertebereich -2.147.483.648 .. 2.147.483.647
BCD (Binary coded decimal) --------------------------
In dieser Variante interpretiert der Prozessor das Low-Nibble (Bits 0..3) eines Bytes als dezimale Ziffer. Das High-Nibble (Bits 4..7) wird bei Ad- ditionen und Subtraktionen ignoriert, bei Multiplikationen und Divisionen muss es Null sein:
BCD 8 Bit, Wertebereich 0 .. 9
PACKED BCD (Packed binary coded decimal) ----------------------------------------
Hierbei handelt es sich um die Erweiterung der normalen BCD. Der Prozessor interpretiert beide Nibbles jeweils als dezimale Ziffer, das LowNibble als Einer-, das HighNibble als Zehnerstelle:
Packed BCD 8 Bit, Wertebereich 0 .. 99
STRING ------
Eine ununterbrochene Sequenz von Bytes, Words oder Doublewords bestimmter Länge. Dieser Datentyp wird meist im Zusammenhang mit den String-Befehlen MOVS, LODS, STOS, CMPS, SCAS, OUTS und INS eingesetzt.
NEAR POINTER ------------
Ein Near Pointer ist der Offset innerhalb eines Segmentes. Die Datenbreite hängt von Prozessortyp und Speichermodell ab und kann 16 oder 32 Bit sein.
FAR POINTER -----------
Die Komponenten eines Far Pointers ergeben zusammen eine logische Adresse. Die Datenbreiten hängen auch hier von Prozessor und Speichermodell ab. Es kann sich z.B. um ein 16 Bit Segment und einen 16 Bit Offset, oder auch um einen 16 Bit Selector und einen 32 Bit Offset handeln.
CPU-Registersatz
Juergen Thelen 2:2450/55.5:
Dieses Topic stellt lediglich einen kleinen Überblick über den Registersatz der Intel 8086 / Pentium Families (& Kompatiblen) dar. Den Registersatz in all seinen Einzelheiten detailliert zu erläutern, würde den Rahmen der FAQ bei weitem sprengen, daraus liesse sich wohl ein ganzes Buch machen... ;)
Daher also kurze, sehr allgemein gehaltene, Gruppen:
- Arbeits-, Index, Pointer und Segment Register - Flag Register - Control Register - Debug Register - Test Register - Model Specific Register
Arbeits-, Index, Pointer und Segment Register ---------------------------------------------
Accu AL, AH, AX, (EAX 386+) Base BL, BH, BX, (EBX 386+) Counter CL, CH, CX, (ECX 386+) Data DL, DH, DX, (EDX 386+) Source Index SI, (ESI 386+) Destination Index DI, (EDI 386+) Base Pointer BP, (EBP 386+) Stack Pointer SP, (ESP 386+) Instruction Pointer IP, (EIP 386+) Code Segment CS Data Segment DS Extra Segment ES Help Segment 1 FS (386+) Help Segment 2 GS (386+) Stack Segment SS
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1| | | | | | | | | | | | | | | | | |f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0|f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+----- ?H ------+----- ?L ------+ +-------------- ?X -------------+ +-------------- ?I -------------+ +-------------- ?P -------------+ +-------------- ?S -------------+ +------------------------------ E?X ----------------------------+ 386+ +------------------------------ E?I ----------------------------+ 386+ +------------------------------ E?P ----------------------------+ 386+
Die Register AX, BX, CX, DX, SI, DI, BP und SP (bzw. E?? ab 386+) können für nahezu alle arithmetischen und logischen Operationen des Prozessors verwen- det werden. Desweiteren werden sie zur Berechnung von Adressen und Speicher- zugriffe lesender und schreibender Art eingesetzt.
Einige Register bzw. Registerpaare sind zudem für ganz bestimmte Operationen prädestiniert bzw. fest an sie gebunden, z.B.:
CS:eIP Zeiger auf die nächste auszuführende Instruction SS:eSP Zeiger auf das nächste Stackelement
DS:SI, ES:DI Stringoperationen LODS, STOS, MOVS, CMPS, SCAS, INS, OUTS
AL, AX, EAX Stringoperationen LODS, STOS ...
CX LOOP-Instruction
DX:AX DIV/IDIV/MUL/IMUL (Double Precision)
Weitere Informationen über die Registerbindung spezifischer Instructions können dem Topic BAW0009 (CPU-Befehlssatz) entnommen werden.
Flag Register -------------
+----------- FLAGS -------------+ +---------------------------- EFLAGS ---------------------------+ 386+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1|1|1|1|1|1|1| | | | | | | | | | | | | | | | |f e d c b a 9 8 7 6|5|4|3|2|1|0|f|e|d c|b|a|9|8|7|6|5|4|3|2|1|0| | | | | | | | | | | | | | | | | | | | | | | | | |C| | | | | | | | | | | | | | | | | | | | | | |P| | | | | | | | I | | | | | | | | | | | | | | |U|V|V| | | | | | O | | | | | | | | | | | | | | |I|I|I|A|V|R| |N| P |O|D|I|T|S|Z| |A| |P| |C| |0 0 0 0 0 0 0 0 0 0|D|P|F|C|M|F|0|T| L |F|F|F|F|F|F|0|F|0|F|1|F| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+---+-+-+-+-+-+-+-+-+-+-+-+-+
0 = reserviert 1 = reserviert CF = Carry Flag PF = Parity Flag AF = Auxiliary Flag ZF = Zero Flag SF = Sign Flag TF = Trap Flag IF = Interrupt Enable Flag DF = Direction Flag OF = Overflow Flag IOPL = I/O Privilege Level (286+) NT = Nested Task Flag (286+) RF = Resume Flag (386+) VM = Virtual 86 Mode Flag (386+) AC = Alignment Check Mode Flag (486+) VIF = Virtual Interrupt Flag (P5+, neuere 486) VIP = Virtual Interrupt Pending Flag (P5+, neuere 486) CPUID = CPUID instruction support Flag (P5+, neuere 486)
Control Register ----------------
Control Register CR0 (bzw. Machine Status Word MSW, 286) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+------------ MSW --------------+ 286+ +----------------------------- CR0 -----------------------------+ 386+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|1|1|1 1 1 1 1 1 1 1 1 1|1|1|1| | | | | | | | |f|e|d|c b a 9 8 7 6 5 4 3|2|1|0|f e d c b a 9 8 7 6|5|4|3|2|1|0| | | | | | | | | | | | | | | | |P|C|N| |A| |W| |N|E|T|E|M|P| |G|D|W|0 0 0 0 0 0 0 0 0 0|M|0|P|0 0 0 0 0 0 0 0 0 0|E|T|S|M|P|E| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 = reserviert PE = Protection Enable (286+) MP = Monitor Coprocessor (286+) EM = Emulation (286+) TS = Task switched (286+) ET = Extension Type (386+) NE = Numeric Error (486+) WP = Write Protect (486+) AM = Alignment Mask (486+) NW = No writethrough (486+) CD = Cache disable (486+) PG = Paging (386+)
Control Register CR1 ++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386+ | reserviert | +---------------------------------------------------------------+
Undokumentiert 386: Löst bei Verwendung Int 6 (Invalid) aus.
Control Register CR2 ++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1|1 1 1 1 1 1 | | |f e d c b a 9 8 7 6|5 4 3 2 1 0 f e d c|b a 9 8 7 6 5 4 3 2 1 0| | | | | 386+ | PDI | PTI | PAB0B | | | | Page Fault Linear Address | +---------------------------------------------------------------+
CR2 dient der Unterstützung der Fehlerbehandlung von Page Faults. Ist Pa- ging aktiv (CR0.PG=1), speichert der Prozessor in CR2 die lineare Adresse, die einen Page Fault (#PF, Int 14, siehe BAW0017) verursacht.
PDI enthält dabei den Page Directory Index womit sich über CR3 die Adresse des involvierten Page Tables, PTI den Page Table Index, womit sich der be- troffene Page Table Entry ermitteln läßt. PAB0B enthält die untersten 12 Bit, die beim 4 KByte-Paging zusammen mit den oberen 20bit des Page Table Entries normalerweise die definierte physikalische 32bit-Adresse ergeben.
Control Register CR3 ++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c|b a 9 8 7 6 5|4|3|2 1 0| | | | | | | | PDB | |P|P| | 386+ | | |C|W| | | Page Directory Base |0 0 0 0 0 0 0|D|T|0 0 0| +---------------------------------------+-+-+-+-+-+-+-+-+-+-+-+-+
CR3 (auch PDBR, Page Directory Base Register, genannt) dient der Steuerung des Pagings (CR0.PG=1). Die PDB-Bits werden in diesem Betriebsmodus als oberste 20bit (untere 12 sind immer 0) der 32bit-Adresse des Page Directo- ries interpretiert (enthält die Adressen der Page Tables).
HINWEIS: Das Beschreiben von PDB löscht den Page Level Cache (und TLB).
PWT = Page-Level Writetrough (486+) PCD = Page-Level Cache Disable (486+)
Control Register CR4 ++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9|8|7|6|5|4|3|2|1|0| | | | | | | | | | | | | |P|P|M|P|P| |T|P|V| 486+ | |C|G|C|A|S|D|S|V|M| |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|E|E|E|E|E|E|D|I|E| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 = reserviert VME = Virtual 86 Mode Extensions (P5+) PVI = Protected Mode Virtual Interrupts (P5+) TSD = Time Stamp Disable (P5+) DE = Debugging Extensions (P5+) PSE = Page Size Extensions (4 MByte Pages) (P5+) PAE = Physical Address Extensions (P6+) MCE = Machine Check Enable (P5+) PGE = Page Global Enable (P6+) PCE = Performance Counter Enable (P6+)
Undokumentiert 386, 486: Löst bei Verwendung Int 6 (Invalid) aus.
Control Register CR5 ++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386+ | reserviert | +---------------------------------------------------------------+
Undokumentiert 386: Löst bei Verwendung Int 6 (Invalid) aus.
Control Register CR6 ++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386+ | reserviert | +---------------------------------------------------------------+
Undokumentiert 386: Löst bei Verwendung Int 6 (Invalid) aus.
Control Register CR7 ++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386+ | reserviert | +---------------------------------------------------------------+
Undokumentiert 386: Löst bei Verwendung Int 6 (Invalid) aus.
Debug Register --------------
Debugregister DR0..DR3 ++++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386+ | Linear Adress Breakpoint n (DRn) | +---------------------------------------------------------------+
Im Zusammenhang mit den entsprechenden Bits von DR7 (Control) kann jedes der Register DR0 bis DR3 zur Definition eines einzelnen Breakpoints ver- wendet werden. In DR0 bis DR3 ist die zu überwachende, lineare Adresse an- zugeben, in den zugehörigen Bits von DR7 die gewünschte Break-Condition.
Debugregister DR4 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386+ | reserviert | +---------------------------------------------------------------+
Undokumentiert 386+: Mirror/Alias für DR6
Debugregister DR5 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386+ | reserviert | +---------------------------------------------------------------+
Undokumentiert 386+: Mirror/Alias für DR7
Debugregister DR6 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1| | | | | | | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0|f|e|d|c b a 9 8 7 6 5 4|3|2|1|0| | | | | | | | | | | 386+ | |B|B|B| |B|B|B|B| |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1|T|S|D|0 1 1 1 1 1 1 1 1|3|2|1|0| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 = reserviert 1 = reserviert B0 = Breakpoint 0 triggered B1 = Breakpoint 1 triggered B2 = Breakpoint 2 triggered B3 = Breakpoint 3 triggered BD = Debug Register Access detected BS = Single Step Break triggered (EFLAGS Trap) BT = Task Switch Break triggered (TSS Trap)
Aus DR6 kann der Status seit dem letzten Breakpoint entnommen werden.
HINWEIS: Der Prozessor löscht niemals DR6 aus eigenem Antrieb, dafür ist der Handler selbst verantwortlich.
Bit Bn (B0 bis B3) in DR6 wird nur gesetzt, wenn die entsprechende Break Condition (DRn, sowie LENn und R/Wn in DR7) erfüllt ist.
BD in DR6 wird gesetzt, wenn die CPU erkennt, daß im nächsten Cycle auf eines der Debugregister DR0 bis DR7 zugegriffen würde (funktioniert natür- lich nur, wenn DR7.GD = 1 ist). BD wird i.d.R. nur von spezieller Hard- und/oder Software, sogenannten In-circuit emulators (wie z.B. Intels ICE), benutzt.
BS in DR6 wird gesetzt, falls eine Single Step Exception ausgelöst wurde, d.h. das TF-Bit (Bit 8) in FLAGS/EFLAGS gesetzt wurde/ist. Single Step hat höchste Debug-Priorität, es könnten also weitere Bits in DR6 gesetzt sein.
BT in DR6 wird gesetzt, wenn beim Taskswitching ein Task aktiviert wurde, der das Trap-Bit im TSS (Task State Segement) gesetzt hat.
Debugregister DR7 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1|1 1|1 1|1 1|1 1|1 1|1 1|1 1| | | | | | | | | | | | | | |f e|d c|b a|9 8|7 6|5 4|3 2|1 0|f e|d|c b a|9|8|7|6|5|4|3|2|1|0| | | | | | | | | | | | | | | | | | | | | | | | L | R | L | R | L | R | L | R | | | | | | | | | | | | | | 386+ | E | / | E | / | E | / | E | / | | | | | | | | | | | | | | | N | W | N | W | N | W | N | W | |G| |G|L|G|L|G|L|G|L|G|L| | 3 | 3 | 2 | 2 | 1 | 1 | 0 | 0 |0 0|D|0 0 1|E|E|3|3|2|2|1|1|0|0| +---+---+---+---+---+---+---+---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 = reserviert 1 = reserviert L0 = Local Enable Breakpoint 0 G0 = Global Enable Breakpoint 0 L1 = Local Enable Breakpoint 1 G1 = Global Enable Breakpoint 1 L2 = Local Enable Breakpoint 2 G2 = Global Enable Breakpoint 2 L3 = Local Enable Breakpoint 3 G3 = Global Enable Breakpoint 3 LE = Local Exact Data Breakpoint Match GE = Global Exact Data Breakpoint Match GD = Global Debug Enable R/W0 = Break Condition Breakpoint 0 LEN0 = Length of monitored item for Breakpoint 0 R/W1 = Break Condition Breakpoint 1 LEN1 = Length of monitored item for Breakpoint 1 R/W2 = Break Condition Breakpoint 2 LEN2 = Length of monitored item for Breakpoint 2 R/W3 = Break Condition Breakpoint 3 LEN3 = Length of monitored item for Breakpoint 3
Im Zusammenhang mit den Registern DR0 bis DR3 können über DR7 Breakpoints definiert werden. Dazu sind die DRn entsprechenden Ln, Gn, R/Wn und LENn in DR7 anzugeben.
R/Wn definiert die Aktion, die den Breakpoint auslöst:
00 nur bei Ausführung der Instruction an Adresse DRn 01 nur bei Schreibzugriff auf Adresse DRn 10 bis 486: reserviert P5+: nur bei I/O Zugriffen (R/W) auf Port DRn (CR4.DE=1) 11 bei Schreib- oder Lesezugriff auf Adresse DRn
LENn definiert die Operandengröße an Adresse DRn:
00 ein Byte 01 zwei Byte 10 reserviert 11 vier Byte
Aktiviert wird ein Breakpoint durch Setzen des entsprechen Ln- oder Gn-Bit in DR7. Ln aktiviert einen lokalen (nur aktueller Task), Gn einen globalen Breakpoint (wirkt in allen Tasks).
Durch Setzen von LE (lokal, aktueller Task)) oder GE (global, alle Tasks) wird bei Data Breakpoints die Adresse der verursachenden Instruction ge- liefert.
Test Register -------------
HINWEIS: Diese Register können NUR auf 386 und 486 CPUs direkt adressiert werden (MOV TRn, x bzw. MOV x, TRn). Ab P5ern führt die Benutzung entsprechender Instructions zu Int 6 (Invalid). Statt mit den al- ten Instructions greift man ab P5 über die MSRs (Model Specific Registers) auf den TLB (Transfer Lookaside Buffer) zu.
Beim TLB handelt es sich um einen Cache, über den lineare in physikalische Adressen konvertiert werden.
Bei der Verwendung von TRs sollte Paging deaktiviert sein (CR0.PG = 0), da es sonst leicht zu Kollisionen mit den über TRn in den TLB geschriebenen Daten kommen kann.
Test Register TR0 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | | reserviert | +---------------------------------------------------------------+
Undokumentiert 386: Löst bei Verwendung Int 6 (Invalid) aus.
Test Register TR1 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | | reserviert | +---------------------------------------------------------------+
Undokumentiert 386: Löst bei Verwendung Int 6 (Invalid) aus.
Test Register TR2 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | | reserviert | +---------------------------------------------------------------+
Undokumentiert 386: Löst bei Verwendung Int 6 (Invalid) aus.
Test Register TR3 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 486 | Internal Cache | +---------------------------------------------------------------+
Undokumentiert 386: Löst bei Verwendung Int 6 (Invalid) aus.
Test Register TR4 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386/486 | Internal Cache | +---------------------------------------------------------------+
Undokumentiert 386: Mirror/Alias TR5. Aus diesem Register kann die 32bit- Adresse ausgelesen werden, die beim nächsten Prefetch auf dem CPU-Bus erscheint. Durch Nachhalten der Anzahl TR4-Änderungen bei der Ausführung von Instructions ist so die Größe bzw. Granularity der Prefetch-Unit ermit- telbar. Sehr hilfreich für das Instruction Scheduling, da man somit den "Low-Water-Point" (Laden des Prefetch Buffers) der CPU kennt... ;)
Undokumentiert 486: Auch hier Mirror/Alias TR5. Allerdings etwas schwerer zu handhaben, da TR4 auf 486ern ja offiziell für einen ganz anderen Zweck eingesetzt wird. Wen interessiert, wie's trotzdem geht: [QLL0001]...
Test Register TR5 +++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | 386/486 | Internal Cache | +---------------------------------------------------------------+
Undokumentiert 386: Mirror/Alias TR4. Nutzen wie bei TR4 beschrieben. Undokumentiert 486: Mirror/Alias TR4. Nutzen wie bei TR4 beschrieben.
Test Register TR6 (Command) +++++++++++++++++++++++++++
+----------------------------- TR6 -----------------------------+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c|b|a 9|8 7|6 5|4 3 2 1|0| | | | | | | | | | | | D | U | R | | | 386/486 | | | T | / | / | |T| | Linear Address (20 MSBs) |V| Y | S | W |0 0 0 0|C| +---------------------------------------+-+---+---+---+-+-+-+-+-+
0 = reserviert
TC = Test Command: TLBWrite (0) oder TLBLookup (1)
R/W = Read/Write: 00 reserviert 01 TLBWrite HiBit wird 0 nach Write TLBLookup Match, wenn HiBit = 0 10 TLBWrite HiBit wird 1 nach Write TLBLookup Match, wenn HiBit = 1 11 reserviert
U/S = User/Supervisor: 00 reserviert 01 TLBWrite HiBit wird 0 nach Write TLBLookup Match, wenn HiBit = 0 10 TLBWrite HiBit wird 1 nach Write TLBLookup Match, wenn HiBit = 1 11 reserviert
DTY = Dirty: 00 reserviert 01 TLBWrite HiBit wird 0 nach Write TLBLookup Match, wenn HiBit = 0 10 TLBWrite HiBit wird 1 nach Write TLBLookup Match, wenn HiBit = 1 11 reserviert
V = Valid Bit
Test Register TR7 (Data) ++++++++++++++++++++++++
+----------------------------- TR7 -----------------------------+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c|b a 9 8 7 6 5|4|3 2|1 0| | | | | | | | | |H| R | | 386/486 | | |I| E | | | Physical Address (20 MSBs) |0 0 0 0 0 0 0|T| P |0 0| +---------------------------------------+-+-+-+-+-+-+-+-+---+-+-+
0 = reserviert
REP = Report: TLBWrite TLBBlockSelect (0..3) TLBLookup TLBBlockHit (0..3, nur wenn HT = 1)
HIT = Hit: TLBWrite immer 1 TLBLookup Hit(1) oder Miss(0)
Model Specific Registers ------------------------
HINWEIS: Diese Register existieren erst ab P5 CPUs. Intel deklariert die MSRs als rein modellspezifisch, d.h. Funktion und Inhalt jedes einzelnen MSRs kann auch innerhalb ein- und derselben Prozessor- generation voneinander abweichen... &)
Auf die MSRs wird fast ausschließlich über RDMSR und WRMSR zuge- gegriffen (ausgenommen RDTSC für Register 16).
Ob RDMSR/WRMSR Instructions überhaupt unterstützt werden, lässt sich an gesetztem Bit 5 in EDX nach einem CPUID(1) erkennen (für RDTSC an gesetztem Bit 4).
HINWEIS: In dieser FAQ wurden nur MSRs aufgenommen, die mit dem P5 einge- führt wurden. P5-modellspezifische (Mask/Revision-abhängige) MSRs und die über 60 zusätzlichen MSRs, die mit dem P6 (PPro) hinzu- kamen, mußten leider unter den Tisch fallen, sonst würde aus der FAQ wohl eine ganze Brockhaus-Reihe... ;)
MCA (P6: P5_MC_ADDR), Machine Check Address, MSR 00H (0), [R-] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2|2 2 2 2 1 | |f e d c b a 9 8 7 c b a 9 8 7 6 5 4|3 2 1 0 f 5 4 3 2 1 0| | | | P5+ | reserviert | verursachende Adresse | +------------------ : ------------------+--------- : -----------+
In diesem Register wird in bestimmten Fehlersituationen (vollkommen unab- hängig vom Zustand von CR4.MCE!) die Adresse des Cycles abgelegt, der die Fehlersituation verursacht hat. Diese Fehlersituationen sind in der Regel so schwerwiegend (z.B. Data Parity Error in einem Read Cycle), daß mit der Programmausführung nicht fortgefahren werden kann.
Auf P5ern liegt die Adresse generell in physikalischer Form vor, auf P6ern ist das Format von der Fehlerursache abhängig und kann 32bit linear, 32bit virtuell, oder 36bit physikalisch sein.
Ist CR4.MCE = 1, löst die CPU nach dem Füllen von MCA und MCT Interrupt 18 (#MC, Machine Check Exception) aus.
HINWEIS: MCA ist nur dann gültig, wenn auch MCT.CHK gesetzt ist.
MCT (P6: P5_MC_TYPE), Machine Check Type, MSR 01H (1), [R-] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 | | | | | | |f e d c b a 9 8 7 6 5 4 3 1 0 f e d c b a 9 8 7 6 5|4|3|2|1|0| | | | | | | | | |L|M| | | | P5+ | |O|/|D|W|C| | |C|I|/|/|H| | reserviert |K|O|C|R|K| +------------------------- : -------------------------+-+-+-+-+-+
CHK = MSR.MCA und MSR.MCT gültig (1) oder nicht (0) W/R = Fehler während Write Cycle (1) oder Read Cycle (0) D/C = Fehler während Data Fetch (1) oder Code Fetch (0) M/IO = Fehler während Memory Cycle (1) oder I/O Cycle (0) LOCK = LOCK während des Fehlers aktiv (1) oder inaktiv (0)
HINWEIS: Durch das Lesen von MCT wird automatisch MCT.CHK gelöscht. Dieses Verhalten kann leicht zu Fehlinterpretationen in Hinsicht auf die Gültigkeit der MSRs MCA und MCT führen.
TR1 (PRR), Parity Reversal Register, MSR 02H (2), [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 1 1 1 1 1 1 | | | | | | | | | | | | | | | |f e d c b a 9 8 5 4 3 2 1 0 f e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| | | | | | | | | | | | | | | | | | | |D|D| | |I|I|I|I|I|I| | |P| P5+ | |M|T|T|D|D|T|T|D|D|D|D|I|N|E| | reserviert |C|D|T|D|T|D|T|3|2|1|0|T|S|S| +---------------- : ----------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
PES [RW] = Parity Error Summary, wird bei jedem Parity Error gesetzt NS [-W] = No Shutdown (1) oder Shutdown (0) bei Parity Error IT [-W] = Instruction Tag ID0 [-W] = Instruction Data Lo Even Bits ( 0, 2, ..., 124, 126) ID1 [-W] = Instruction Data Lo Odd Bits ( 1, 3, ..., 125, 127) ID0 [-W] = Instruction Data Hi Even Bits (128, 130, ..., 252, 254) ID1 [-W] = Instruction Data Hi Odd Bits (129, 131, ..., 253, 255) ITT [-W] = Instruction Tag TLB ITD [-W] = Instruction Tag Data DT [-W] = Data Tag DD [-W] = Data Data DTT [-W] = Data Tag TLB DTD [-W] = Data Tag Data MC [-W] = Microcode, umgekehrte Parität bei Reads
Durch Setzen eines Bits im Bereich IT..DTD (Bits 2..12) lässt sich der je- weilige Sinn der Paritätserzeugung exakt umkehren (gilt sowohl für Cache Replacements, als auch für Data und Testability Writes).
Um für Microcode Paritätsfehler erzwingen zu können, setzt man PRR.MC = 1. Bei nächsten Read wird dadurch der gewünschte Fehler erzeugt.
MSR 03H (3) +++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 | |f e d c b a 9 8 7 6 5 4 3 2 1 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | P5+ | reserviert | +----------------------------- : -------------------------------+
TR2 (EBTR), End Bit Test Register, MSR 04H (4), [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 | | |f e d c b a 9 8 7 6 5 4 3 1 0 f e d c b a 9 8 7 6 5 4|3 2 1 0| | | | P5+ | reserviert | EBits | +------------------------- : ---------------------------+-------+
TR2 kann zum Testen des Instruction Caches benutzt werden. Jedes EndBit in TR2 bezieht sich auf das jeweilige Instruction-Byte in TR3. Ein gesetztes EndBit bedeutet, daß es sich beim korrespondierenden Byte in TR3 um das letzte Byte eines Befehles handelt.
TR3 (CDTR), Cache Data Test Register, MSR 05H (5), [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 2 2 2 2 2 2 2|1 1 1 1 1 1 1 | |f e d c b a 9 6 5 4 3 2 1 0|f e d c b a 9 6 5 4 3 2 1 0| | | | P5+ | reserviert | Data | +-------------- : --------------+-------------- : --------------+
Über TR3 können zu Testzwecken Daten aus dem Instruction Cache (L1-Cache) gelesen bzw. in ihn geschrieben werden. Um eine komplette Cache Line lesen bzw. schreiben zu können, sind acht Zugriffe erforderlich, da TR3 nur je- weils 4 Byte (32bit) in einem Rutsch verarbeitet, Cache Lines auf P5+ aber bekanntlich 32 Bytes lang sind.
TR4 (CSTR), Cache Status Test Register, MSR 06H (6), [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+ : +-+-+-+-+ |3 3 3 3 3 3 3 2 2 2 2 2 2 2|1 1 1 1 | | | | |f e d c b a 9 6 5 4 3 2 1 0|f e d c b a 9 8|7 3|2|1 0| | | | | | | | | | |L| V | P5+ | | | |R| A | | reserviert | Tag |reserv.|U| L | +-------------- : --------------+------- : -------+-- : --+-+---+
VAL = Status der Cache Line
Instruction (TR5.CD=1) Data (TR5.CD=0)
00 invalid invalid 01 shared valid 10 exclusive invalid 11 modified valid
LRU = Way 0 (0) oder Way 1 (1)
HINWEIS: Für TestWrites muß TR4 nicht nur zuvor mit den gewünschten Tag/ LRU/VAL-Bits geladen werden, sondern auch noch mit einem TestRead beschickt werden. Erst durch den TestRead erfolgt der eigentliche Update von TR4 mit den gewünschten Bits. Vorsicht: Die obersten 4 TagBits (28..31) werden durch den TestRead gelöscht... ;)
HINWEIS: Bei TestReads mit abgeschaltetem Cache (CR0.CD=1) ändert sich LRU nur beim Lesen des Instruction Cache (TR5.CD=1).
TR5 (CCTR), Cache Control Test Register, MSR 07H (7), [-W] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 | | | | | | | |f e d c b a 9 8 6 5 4 3 2 1 0 f|e|d|c|b a 9 8 7 6 5|4 3 2|1 0| | | | | | | | | | | | | | | | C | | | | |E| | B | N | P5+ | |W|C|N| | U | T | | reserviert |B|D|T| SET | F | L | +--------------- : ---------------+-+-+-+-------------+-----+---+
CNTL = Control
00 Normalbetrieb 01 TestWrite 10 TestRead 11 Flush
BUF = Buffer, Nummer (0..7) des gewünschten 4-Byte Block der Cache Line, der über TR3 sichtbar sein soll.
SET = Set Address, Nummer (0..127) des gewünschten Cache-Sets
ENT = Entry, Way 0 (0) oder Way 1 (1)
CD = Instruction (Code) Cache (0) oder Data Cache (1)
WB = nur Writethrough (0) oder auch Writeback (1) zulässig
HINWEIS: Writeback (TR5.WB=1) funktioniert nur mit Data Caches... ;)
HINWEIS: TR5.CD (Code/Data Cache) bitte nicht mit CR0.CD (Cache disable) verwechseln... &)
TR6 (TCTR), TLB Command Test Register, MSR 08H (8), [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+ : +-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+ : +-+-+-+-+ |3 3 3 3 3 2 2 2 2 2|1 1 1 1 | | | | | | | | | |f e d c b 4 3 2 1 0|f e d c f e d c|b|a|9|8|7 3|2|1|0| | | | | | | | | | | | | | | | | | | |P|C|O| P5+ | reserviert | Linear Address |V|D|U|W|reserv.|S|D|P| +---------- : ----------+------- : -------+-+-+-+-+-- : --+-+-+-+
OP = Operation, TLBWrite (0) oder TLBRead(1) CD = Instruction (Code) TLB (0) oder Data TLB (1) PS = Page Size 4 KByte (0) oder 4 MByte (1) W = Writes allowed (1) oder nicht (nur Reads) (0) U = Userprivileg (CPL0..3) (0) oder Supervisorprivileg (nur CPL0) (1) D = Page dirty (1) oder nicht (0) V = Entry valid (1) oder nicht (0)
HINWEIS: Beim Lesen des Instruction (Code) TLB (TR5.CD=0) wird automatisch die lineare Adresse in TR6 gelöscht (Bits 12..31)
HINWEIS: TR6.PS macht nur für Data TLBs Sinn. Der Data Cache arbeitet mit einem 64x4 KByte, oder 8x4 MByte TLB, der Instruction Cache immer nur mit einem 32x4 MByte TLB.
TR6.W und TR6.D funktionieren ebenfalls nur mit Data TLBs... ;)
TR7 (TDTR), TLB Data Test Register, MSR 09H (9), [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+ : +-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 2 2 2 2|1 1 1 1 | | | | | | | | | | |f e d c 3 2 1 0|f e d c f e d c|b|a|9|8|7|6 5|4|3 2|1 0| | | | | | | | | | | | | | | |P|P| | | | | | E | | P5+ | | |C|W|L|L|L| | | N | | | reserviert | Physical Address |D|T|2|1|0|0 0|H| T |0 0| +-------- : --------+-------- : --------+-+-+-+-+-+-+-+-+---+-+-+
0 = reserviert ENT = TestWrite: zu nutzender Way, TestRead: Way des Read Hits H = nur TestReads: TR6.LinearAddr im TLB gefunden (1) oder nicht (0) L0..2 = zu ersetzender Eintrag (dem Pseudo-LRU-Algorithmus entsprechend) PWT = Page Level Writetrough (1) oder nicht (0) PCD = Page Level Cache Disable (1) oder nicht (0)
HINWEIS: TLBRead Hits und TLBWrites beeinflussen die LRU-Bits (L0,L1,L2).
MSR 0aH (10) ++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 | |f e d c b a 9 8 7 6 5 4 3 2 1 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | P5+ | reserviert | +----------------------------- : -------------------------------+
TR9, BTB Tag Test Register, MSR 0bH (11), [RW] ++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 2 2 2 2 2 2 2|1 1 1 1 | | | |f e d c b a 9 6 5 4 3 2 1 0|f e d c 9 8 7 6|5 4 3 2|1 0| | | | | | | | | | H | P5+ | | | | I | | reserviert | Tag Address | res. | S | +-------------- : --------------+-------- : --------+-------+---+
HIS = History
TR10, BTB Target Test Register, MSR 0cH (12), [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 2 2 2 2 2 2 2|1 1 1 1 1 1 1 | |f e d c b a 9 6 5 4 3 2 1 0|f e d c b a 9 6 5 4 3 2 1 0| | | | P5+ | reserviert | Target Address | +-------------- : --------------+-------------- : --------------+
TR11 (BCTR), BTB Command Test Register, MSR 0dH (13), [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 1 1 1 1 1 | | | | | |f e d c b a 9 8 7 4 3 2 1 0 f e d c|b a 9 8 7 6|5 4|3 2|1 0| | | | | | | | | | | | C | | | | | E | N | P5+ | | | | N | T | | reserviert | SET |0 0| T | L | +------------------ : ------------------+-----------+-+-+---+---+
0 = reserviert
CNTL = Control
00 Normalbetrieb 01 TestWrite 10 TestRead 11 Flush
ENT = Entry, Nummer (0..3) des Ways innerhalb des Instruction Cache-Sets
SET = Set Address, Nummer (0..63) des gewünschten Instruction Cache-Sets
HINWEIS: Bei Tests des BTB (Branch Target Buffer) sollte TR12.NBP=1 sein, sonst kommt es logischerweise zu den kuriosesten Ergebnissen.. ;)
TR12 (NFCR), New Feature Control Register, MSR 0eH (14), [-W] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 3 1 1 1 1 | | | | | | | |f e d c b a 9 8 7 6 3 2 1 0 f e d c b a|9|8 7 6 5 4|3|2|1|0| | | | | | | | | | |I| | | | |N| P5+ | |T| |C|S|T|B| | reserviert |R| reserv. |I|E|R|P| +-------------------- : --------------------+-+---------+-+-+-+-+
NBP = No Branch Prediction (1) oder Branch Prediction (0) TR = Special Cycle für Branch Traces generieren (1) oder nicht (0) SI = Single Pipe Execution (disable V-Pipe) (1) oder Pairing (0) CI = Cache Inhibit (disable L1-Cache) (1) oder nicht (0) ITR = I/O Trap Restart (im SMM) (1) oder nicht (0)
HINWEIS: Um die Branch Prediction vollkommen abzuschalten, sollte nach ei- nem TR12.NBP=1 zusätzlich der BTB durch Laden von CR3 geflushed werden, sonst werden ältere Predictions nach wie vor benutzt.. &)
HINWEIS: TR12.TR=1 ergibt nur bei Einsatz von externen Hardware-Debuggern einen Sinn.
MSR 0fH (15) ++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 | |f e d c b a 9 8 7 6 5 4 3 2 1 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | P5+ | reserviert | +----------------------------- : -------------------------------+
TSC, Time Stamp Counter, MSR 10H (16), [RW] +++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 | |f e d c b a 9 8 7 6 5 4 3 2 1 f e d c b a 9 8 7 6 5 4 3 2 1 0| | | P5+ | Time Stamp Counter | +----------------------------- : -------------------------------+
Ob die CPU dieses Register überhaupt unterstützt, lässt sich an einem ge- setzten Bit 4 in EDX nach Durchführung von CPUID(1) erkennen.
Ist das der Fall, wird der TSC bei jedem Reset mit 0 initialisiert und er- höht sich danach kontinuierlich und selbsttätig. Auf P5+ wird der TSC bis heute (PPro) mit jedem Taktzyklus der CPU um eins erhöht, allerdings ga- rantiert Intel nicht dafür, daß das auch in zukünftigen Modellen so sein wird... &)
Die Dauer bis zum Erreichen des maximalen Zählwertes ist somit zwar vor- läufig von der Taktfrequenz der CPU abhängig, dürfte aber wohl immer mehr als ausreichend sein. Selbst ein PPro-200 würde sich dafür mehr als 2.900 Jahre lang einen abbrechen... ;)
Zum Auslesen des TSCs sollte aus Geschwindigkeitsgründen die Instruction RDTSC statt RDMSR benutzt werden. RDTSC ist mehr als 100% schneller als RDMSR und kann bei jedem CPL benutzt werden. Bei CPL1..3 jedoch nur dann, wenn kein böser Bube CR4.TSD=1 (Time Stamp Disable) gesetzt hat... 8)
Eine WRTSC Instruction gibt es leider nicht, zum manuellen Setzen des TSCs muß man sich daher mit dem WRMSR-Befehl begnügen.
CESR, Control and Event Select Register, MSR 11H (17), [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+-+-+-+- : -+-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 1 1 1 1|1|1 1 1|1 1 1 1 1 1| | | | | |f e d c d c b a|9|8 7 6|5 4 3 2 1 0|f a|9|8 7 6|5 4 3 2 1 0| | | | | | | | | | P5+ | |P| | | |P| | | | |C| | | |C| | | | reserviert |1| CC1 | ES1 |0 0|0| CC0 | ES0 | +------- : -------+-+-----+-----------+- : -+-+-----+-----------+
0 = reserviert ESn = Event Selection für Monitoring von CTRn:
0 00H Data Read Anzahl 1 01H Data Write Anzahl 2 02H Data TLB Miss Anzahl 3 03H Data Read Miss Anzahl 4 04H Data Write Miss Anzahl 5 05H Write Hit to modified/exclusive Cache Line Anzahl 6 06H Data Cache Lines written back Anzahl 7 07H External Snoop Anzahl 8 08H Data Cache Snoop Hit Anzahl 9 09H Memory Access in both Pipes Anzahl 10 0aH Bank Conflict Anzahl 11 0bH Misaligned Memory or I/O Reference Anzahl 12 0cH Code Read Anzahl 13 0dH Code TLB Miss Anzahl 14 0eH Code Cache Miss Anzahl 15 0fH Segment Register Load Anzahl ± 16 10H Segment Descriptor Cache Access Anzahl ± 17 11H Segment Descriptor Cache Hit Anzahl 18 12H Taken Branch Anzahl 19 13H BTB Hit Anzahl 20 14H Taken Branch or BTB Hit Anzahl 21 15H Pipeline Flush Anzahl 22 16H Instruction Execution Anzahl 23 17H Instruction Execution in V-Pipe Anzahl 24 18H Bus Cycle in progress Dauer 25 19H Stall due to full Write Buffers Dauer 26 1aH Stall due to reading Memory Dauer 27 1bH Stall due to writing modif./excl. Cache Line Dauer 28 1cH Locked Bus Cycle Anzahl 29 1dH I/O Read or I/O Write Cycle Anzahl 30 1eH Non-cacheable Memory Read Anzahl 31 1fH Stall due to Address Generation Interlock Dauer 32 20H reserviert - 33 21H reserviert - 34 22H Floating Point Operation Anzahl 35 23H DR0 Breakpoint Match Anzahl 36 24H DR1 Breakpoint Match Anzahl 37 25H DR2 Breakpoint Match Anzahl 38 26H DR3 Breakpoint Match Anzahl 39 27H Hardware Interrupt Anzahl 40 28H Data Read or Data Write Anzahl 41 29H Data Read Miss or Data Write Miss Anzahl
± undokumentiert
CCn = Counter Control für CTRn:
000 count Event disabled 001 count Event while CPL0..2 010 count Event while CPL3 011 count Event while CPL0..3 100 count Clocks disabled 101 count Clocks while CPL0..2 110 count Clocks while CPL3 111 count Clocks while CPL0..3
PCn = Pin Control Signal CTRn.PMn:
0 on Increment 1 on Overflow
CTR0, Counter 0, MSR 12H (18), [RW] +++++++++++++++++++++++++++++++++++ +-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 2 2 2 2 2|2 2 2 2 2 2 2 2 | |f e d c b c b a 9 8|7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0| | | | P5+ | reserviert | Counter | +---------- : ----------+---------------------------------------+
CTR1, Counter 1, MSR 13H (19), [RW] +++++++++++++++++++++++++++++++++++ +-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+-+-+-+ : +-+-+-+-+-+-+-+-+-+-+ |3 3 3 3 3 2 2 2 2 2|2 2 2 2 2 2 2 2 | |f e d c b c b a 9 8|7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0| | | | P5+ | reserviert | Counter | +---------- : ----------+---------------------------------------+
CPU-Befehlsformat
Juergen Thelen 2:2450/55.5:
Ein Prozessorbefehl setzt sich aus bis zu neun Komponenten zusammen:
Instruction Prefix 0 oder 1 Byte Address-Size Prefix 0 oder 1 Byte Operand-Size Prefix 0 oder 1 Byte Segment Prefix 0 oder 1 Byte Opcode 1 oder 2 Byte Mod R/M 0 oder 1 Byte SIB, Scale Index Base (386+) 0 oder 1 Byte Displacement 0, 1, 2 oder 4 Byte (4 nur 386+) Immediate 0, 1, 2 oder 4 Byte (4 nur 386+)
Instruction Prefix (f0H, f2H, f3H) ----------------------------------
Diese 1-Byte-Komponente ist nur bei Verwendung von REP? und LOCK vorhanden. Das Byte hat in diesen Fällen einen der folgenden Werte:
f0h LOCK f2h REPNE, REPNZ f3h REP, REPE, REPZ
Address-Size Prefix (67H) -------------------------
Im Real und V86 Mode werden Adressangaben in der Voreinstellung als 16 Bit Adressen behandelt. Im Protected Mode wird die Standardgröße durch das D-Bit im ESD (Executable Segment Descriptor) bestimmt (0 = 16 Bit, 1 = 32 Bit).
Anhand des Address Size Prefix kann ein Befehl zum Übergehen dieser Vorein- stellungen veranlasst werden. Das Address-Size Prefix ist eine 1-Byte-Kompo- nente mit folgendem Wert:
67h Address-Size Override
Siehe dazu auch die Kombinations-Tabelle beim Operand-Size Prefix.
Operand-Size Prefix (66H) -------------------------
Die Voreinstellung der vom Prozessor benutzten Operandengröße hängt von ver- schiedenen Faktoren, wie z.B. vom Instruction Prefix, verschiedenen Stan- dardzuweisungen usw., ab.
Diese Voreinstellungen können (wo zulässig) durch Einsatz des Operand-Size Prefix (1-Byte-Komponente) übergangen werden. Der Prefix hat folgenden Wert:
66h Operand-Size Override
Folgende Tabelle zeigt alle möglichen Kombinationen von D-Bit im ESD, dem Address- und dem Operand-Size Prefix, und deren Auswirkung:
+------- D-Bit im ESD : 1 = gesetzt , 0 = gelöscht | +----- Operand-Size Prefix (66h): 1 = vorhanden, 0 = nicht vorhanden | | +--- Address-Size Prefix (67h): 1 = vorhanden, 0 = nicht vorhanden | | |
Auswirkung der Kombination
0 0 0 16bit Adressen, 16bit Daten 0 0 1 32bit Adressen, 16bit Daten 0 1 0 16bit Adressen, 32bit Daten 0 1 1 32bit Adressen, 32bit Daten
1 0 0 32bit Adressen, 32bit Daten 1 0 1 16bit Adressen, 32bit Daten 1 1 0 32bit Adressen, 16bit Daten 1 1 1 16bit Adressen, 16bit Daten
Segment Prefix (26H, 2eH, 36H, 3eH, 64H, 65H) ---------------------------------------------
Speziell bei Datenzugriffen werden vielen Befehlen Standardsegmente zuge- wiesen. Diese Voreinstellungen können (wo zulässig) durch den Einsatz von Segment Prefixes (1-Byte-Komponente) aufgehoben werden:
26h ES Override 2eh CS Override 36h SS Override 3eh DS Override 64h FS Override (386+) 65h GS Override (386+)
Opcode ------
Diese 1- oder 2-Byte Komponente ist in jedem Prozessorbefehl enthalten. Der Opcode (Operation Code) bestimmt, welche Aktion der Prozessor überhaupt aus- führen soll.
Eine ausführliche Beschreibung aller Opcodes befindet sich im Topic BAW0009 (CPU-Befehlssatz).
Mod R/M -------
SIB (Scale Index Base, 386+) ----------------------------
Displacement ------------
Immediate ---------
CPU-Befehlssatz
Juergen Thelen 2:2450/55.5:
Dieses Topic beschäftigt sich mit Erläuterungen zur Funktionsweise der ein- zelnen Befehle (Instructions), deren mögliche Adressierungsarten, ihren Op- codes, beeinflussten Flags, Taktzyklenangaben, möglichen Exceptions u.ä.
Jeder Befehl wird nach dem gleichen Schema beschrieben und beginnt immer mit einem solchen Kasten:
C Carry Flag ----------------------+ P Parity Flag -------------------+ | A Auxiliary Flag --------------+ | | Z Zero Flag -----------------+ | | | S Sign Flag ---------------+ | | | | T Trap Flag -------------+ | | | | | I Interrupt Flag ------+ | | | | | | +-------- privileged (CPL0) D Direction Flag ----+ | | | | | | | | +---- Protected Mode only O Overflow Flag ---+ | | | | | | | |
PRI PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | Mnemonic und Befehlsname |- - - - - 0 1 * ?| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | | | | | - Flag wird vom Befehl nicht beeinflusst -------------------+ | | | | 0 Flag wird vom Befehl gelöscht ------------------------------+ | | | 1 Flag wird vom Befehl gesetzt ---------------------------------+ | | * Flag nach dem Befehl entsprechend dem Ergebnis -----------------+ | ? Flag nach dem Befehl undefiniert ---------------------------------+
Erscheint PRI über dem Befehlskasten, handelt es sich um einen privilegier- ten Befehl. Die Ausführung derartiger Befehle lässt die CPU nur zu, wenn das ausführende Programm CPL0, d.h. den höchsten Privileglevel, besitzt.
Erscheint PMO über dem Befehlskasten, handelt es sich um einen Befehl, den die CPU nur im Protected Mode erkennt und ausführt. Im RM und VM wird dage- gen i.d.R. Interrupt 6 (#UD, Invalid Opcode) ausgelöst.
Nach dem Kasten folgt eine Tabelle mit folgendem Format:
Befehl Pipe P5 486 386 286 86 Opcode
AND AL, imm8 UV ? 1 2 3 4 24 ib
BEFEHL
In dieser Spalte werden alle möglichen Adressierungsarten des jeweiligen Befehles aufgeführt. Dabei werden folgende Abkürzungen verwendet:
imm8 8bit-Konstante (ShortInt, signed Byte) imm16 16bit-Konstante (Integer, signed Word) imm32 32bit-Konstante (LongInt, signed DWord) r8 8bit-Register AL, AH, BL, BH, CL, CH, DL oder DH r16 16bit-Register AX, BX, CX, DX, SI, DI, BP oder SP r32 32bit-Register (386+) EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP m8 8bit-Speicheroperand für String ?SB (DS:eSI oder ES:eDI) m16 16bit-Speicheroperand für String ?SW (DS:eSI oder ES:eDI) m32 32bit-Speicheroperand für String ?SD (DS:eSI oder ES:eDI) m64 64bit-Speicheroperand (CMPXCHG8B) r|m8 8bit-Operand (Register oder Byte aus Speicher) r|m16 16bit-Operand (Register oder Word aus Speicher) r|m32 32bit-Operand (Register oder DWord aus Speicher) rel8 8bit-Offset (relativ, signed Byte) rel16 16bit-Offset (relativ, signed Word) rel32 32bit-Offset (relativ, signed DWord) ptr16:16 segmentierte Zeigerangabe 16bit:16bit (Segment:Offset) ptr16:32 segmentierte Zeigerangabe 16bit:32bit (Segment:Offset) m16:16 ein ptr16:16 aus dem Speicher m16:32 ein ptr16:32 aus dem Speicher m16&16 zwei signed Words im Speicher (BOUND) m16&32 ein Word und ein DWord (unsigned) im Speicher (LGDT, LIDT) m32&32 zwei signed DWords im Speicher (BOUND) moffs8 8bit-Offset (relativ, unsigned Byte) moffs16 16bit-Offset (relativ, unsigned Word) moffs32 32bit-Offset (relativ, unsigned DWord) sreg Segmentregister DS, ES, FS, GS oder SS
PIPE
Diese Spalte betrifft nur Pentium Prozessoren. Pentium CPUs arbeiten mit einer Dual Pipe Architecture (DPA). Die DPA ermöglicht die parallele Aus- führung zweier Befehle gleichzeitig, das sogenannte Pairing. Allerdings funktioniert das Pairing nicht ausnahmslos und ohne Einschränkungen.
Die Pairing-Voraussetzungen sind sehr umfangreich und teilweise auch sehr spezifisch (Schlagwörter: No double NP, Reg Contentions (Flow-, Output-, Anti-Dependence), MemBank Conflicts (Data Cache), Inter-Pipe Concurrency).
Das Pairing selbst zu erläutern ist nicht Sinn dieses Topics, daher finden Sie in der Spalte lediglich Angaben zur grundsätzlichen Pairability des jeweiligen Befehles:
NP nicht pairable PU pairable, wenn der Befehl in der U-Pipe ist PV pairable, wenn der Befehl in der V-Pipe ist UV pairable, egal ob in U- oder V-Pipe
P5, 486, 386, 286, 86
In diesen Spalten wird die Anzahl Taktzyklen angegeben, die ein Befehl auf dem jeweiligen Prozessor zur Ausführung benötigt. Die zeitliche Länge ei- nes Taktzyklus hängt von der Taktfrequenz der CPU ab. Sie kann mit einer der folgenden Formeln ermittelt werden:
für Einheit Formel
Sekunde 1 / (Hz Ihres Rechners) Nanosekunde 1000 MHz / (MHz Ihres Rechners)
Bei allen Taktzyklenangaben handelt es sich um bereinigte Werte, d.h. es wurden folgende Bedingungen zugrunde gelegt:
- Buszugriffe benötigen keine WAIT STATES.
- Durch die Ausführung des Befehles wird keine Exception ausgelöst.
- Es liegen keine lokalen HOLD Requests an.
- Cache Hit bei jedem Sprungziel-, Daten- oder Befehlszugriff.
- Speicheroperanden sind aligned (auf optimale 0MODx Boundaries).
- keine AGIs bei EA-Berechnungen.
- TLB Hit bei jeder Page Translation.
Bei manchen Befehlen tauchen in den Taktzyklenspalten zusätzliche Abkürz- ungen auf:
EA Zusatzzyklen für Effective Address Computation TS Zusatzzyklen für Task Switch n Anzahl Wiederholungen m Anzahl Komponenten (Displacements und Immediates zählen als je 1) PM Zyklenangabe gilt für Protected Mode VM Zyklenangabe gilt für Virtual 86 Mode
EA - Effective Address Computation
Auf 8086ern muß bei einigen Befehlen bzw. deren Adressierungsarten, erst noch eine effektive Adresse vor der Ausführung berechnet werden. Dieser Vorgang benötigt natürlich einige zusätzliche Taktzyklen. Obwohl wir uns eigentlich im Zeitalter der P5er bewegen, der Vollständigkeit halber:
Adressierungsart Zusatzzyklen
Base, Index +5 Disp +6
Base+Index (BX+SI|BP+DI) +7 Base+Index (BX+DI|BP+SI) +8
Base+Disp, Index+Disp +9
Base+Index+Disp (BX+SI+Disp|BP+DI+Disp) +11 Base+Index+Disp (BX+DI+Disp|BP+SI+Disp) +12
TS - Task Switch
Löst ein Befehl einen Task Switch in ein anderes Segment aus, dann müssen natürlich auch entsprechende Zusatzzyklen miteinkalkuliert werden. Wieviel Zusatzzyklen dies sind, hängt von der Art des neuen TSS und davon ab, ob der alte und/oder neue Task sich im V86-Mode befinden, oder nicht. Die TS- Zeiten finden Sie vor dem Pseudocode des jeweiligen Mnemonics (CALL, INT, INTO, IRET, IRETD, JMP).
OPCODE
In dieser Spalte finden Sie die dem jeweiligen Befehl entsprechenden Op- codes (hex). Zusätzlich werden folgende Abkürzungen verwendet:
ib dem Opcode, ModR/M- oder SIB-Byte folgt eine 8bit-Konstante iw dem Opcode, ModR/M- oder SIB-Byte folgt eine 16bit-Konstante id dem Opcode, ModR/M- oder SIB-Byte folgt eine 32bit-Konstante /Ziffer der Befehl verwendet nur das Feld r/m des ModR/M Bytes. Die Ziffer (0..7) im Feld reg des ModR/M Bytes bestimmt, welche Art Erweiterung der Opcode bekommt. /r der Befehl verwendet die Felder r/m und reg des ModR/M Bytes cb dem Opcode folgt ein 8bit-Offset (unsigned) cw dem Opcode folgt ein 16bit-Offset (unsigned) cd dem Opcode folgt ein 32bit-Offset (unsigned) cp dem Opcode folgt ein 48bit-Offset (unsigned) +rb Registercode (0..7 = AL, CL, DL, BL, AH, CH, DH, BH) +rw Registercode (0..7 = AX, CX, DX, BX, SP, BP, SI, DI) +rd Registercode (0..7 = EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI)
PSEUDOCODE
Verwendung einer an eine Hochsprache angelehnten Syntax zur Kurzbeschrei- bung des jeweiligen Befehles. Verwendete Symbole:
DEST Zieloperand (bei Intel normalerweise der 1. Operand) SRC Quelloperand (bei Intel normalerweise der 2. Operand) SRC1 1. Quelloperand (bei mehreren Quelloperanden) SRC2 2. Quelloperand (bei mehreren Quelloperanden) CPL Current Privilege Level RPL Requestors Privilege Level DPL Descriptors Privilege Level IOPL I/O Privilege Level ?F Entsprechendes Flag (z.B. CF = Carry Flag usw.) ?L, ?H, ?X Entsprechende Register (z.B. AL, AH, AX, EAX usw.)
HINWEIS: Intel weist paradoxerweise der höchsten logischen Privilegstufe den niedrigsten mathematischen Wert zu. In den Pseudocodes werden Privilegstufen (CPL, DPL, RPL, IOPL) jedoch immer rein mathema- tisch verglichen. Dieser Gegensatz kann einen natürlich deftig verwirren, wenn man nicht immer dran denkt... ;)
BESCHREIBUNG
Ausführliche Beschreibung des Befehls und eventueller Besonderheiten.
EXCEPTIONS
Angaben zu möglichen Exceptions bei der Anwendung des jeweiligen Befehles im Real Address (RM), Virtual 86 (VM) oder Protected Mode (PM).
Ausführliche Beschreibungen der einzelnen Exceptions finden Sie im Topic BAW0017 (Exceptions).
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | AAA - ASCII Adjust AL after Addition |? - - - ? ? * ? *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
AAA NP 3 3 4 3 8 37
Pseudocode:
if AL > f9H then Tmp = 1 else Tmp = 0 endif
if ((AL AND 0fH) > 9) OR (AF = 1) then AL = (AL + 6) AND 0fH AH = AH + 1 + Tmp AF = 1 CF = 1 else AF = 0 CF = 0 AL = AL AND 0fH endif
Beschreibung:
Addiert man BCDs mittels ADD, kann es zu einem Stellenüberlauf kommen, der zu falschen BCD-Ergebnissen führt. Die Ursache hierfür ist keine de- fekte CPU ;), sondern die Tatsache, daß Intel x86 CPUs zwar binär, aber eben nicht binär codiert (BCD, Binary coded decimal) rechnen. Dazu ein Beispiel:
mov al, 00001001B ; 09H add al, 00001001B ; 09H ==================== al = 00010010B ; 12H
Bekanntlich haben BCDs nur einen Wertebereich von 0..9 und werden im LoNibble eines 8bit-Registers geführt. Betrachtet man sich jetzt das Lo- Nibble (?2H) des Ergebnisses, fällt sofort auf, daß da bei BCD-Betrach- tungsweise eigentlich (?8H) stehen müsste, denn schließlich ist 9 + 9 ja = 18 und nicht = 12.
Diesen "Fehler" zu justieren, ist Sinn von AAA:
mov al, 00001001B ; 09H add al, 00001001B ; 09H aaa ==================== al = 00001000B ; 08H
Aber was ist mit der Zehnerstelle? Schließlich ist 9 + 9 auch nicht = 8, sondern immer noch = 18...
Sieht man sich den Pseudocode nochmal genauer an, wird offensichtlich, daß AAA bei einem Überlauf auch AH inkrementiert. Initialisiert man nun seine BCD-Ziffern entsprechend, ist auch die Zehnerstelle richtig:
xor ah, ah ; 00H mov al, 00001001B ; 09H add al, 00001001B ; 09H aaa ==================== al = 00001000B ; 08H ah = 00000001B ; 01H
Voila, AX lautet nun 0108H, was bei BCD-Betrachtungsweise = 18 ist... ;)
Übrigens, BCDs haben den Vorteil, daß man durch ein simples "OR r8, 30H" schon die ausgabefertige ASCII-Ziffer erhält...
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | AAD - ASCII Adjust AX before Division |? - - - * * ? * ?| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
AAD NP 10 14 19 14 60 D5 0A ± AAD, imm8 NP 10 14 19 14 60 D5 ??
Pseudocode:
AL = ((AH * 2nd_byte_of_instruction) + AL) AND ffH AH = 0
Beschreibung:
Wie bei der Instruction AAA bereits beschrieben, rechnen Intel x86 CPUs zwar binär, aber nicht binär codiert (BCD, Binary coded decimal).
Daraus folgt, daß eine BCD vor einer Division natürlich zuerst wieder in das "richtige" binäre Format gebracht werden muss, anderenfalls ist mit falschen Divisionsergebnissen zu rechnen. Beispiel:
mov ax, 0108H ; BCD = 18 mov bl, 9 div bl ; / 9 ================ ax = 031dH ; BCD = 3?
Offensichtlich ist das Ergebnis falsch (29, Rest 3), es müßte = 2 sein. Genau deshalb wird hier AAD gebraucht:
mov ax, 0108H ; BCD = 18 aad ; AX = 0012H = 18 (binär) mov bl, 9 div bl ; / 9 ================ ax = 0002H ; BCD = 02
Undokumentiert:
Intel weist erst ab P5-Dokumentationen daraufhin, daß es auch eine Art von "AAD imm8" gibt. Es soll aber Programmierer geben, die "AAD imm8" schon etwas ;) länger verwenden... 8)
Wozu? Nun, der 2. Opcode der AAD Instruction ist nichts anderes als der Multiplikator für AH. Möchte man AH mit einem anderen Wert als 0aH (10) multiplizieren, setzt man einfach den 2. Opcode entsprechend.
HINWEIS: "AAD imm8" eignet sich natürlich nur für Ergebnisse im 8bit- Bereich (< 256).
Der Einsatz von "AAD imm8" bietet zwei Vorteile. Zum einen lässt sich so ein (bis heute leider nicht existentes) "MUL r8, imm8", zumindest einge- schränkt, emulieren. Zum anderen gibt es bestimmte Situationen, in denen "AAD imm8" einfach schneller ist, als entsprechende MUL/ADD Sequenzen.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap) PM #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | AAM - ASCII Adjust AX after Multiply |0 - - - 0 * * * 0| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
AAM NP 18 15 17 16 83 D4 0A ± AAM imm8 NP 18 15 17 16 83 D4 ??
Pseudocode:
if 2nd_byte_of_instruction = 0 then Int 0 else AH = AL / 2nd_byte_of_instruction AL = AL MOD 2nd_byte_of_instruction endif
Beschreibung:
Wie bei der Instruction AAA bereits beschrieben, rechnen Intel x86 CPUs zwar binär, aber nicht binär codiert (BCD, Binary coded decimal).
Daraus folgt, daß auch nach der Multiplikation von BCDs das Ergebnis entsprechend justiert werden muss. Beispiel:
mov al, 9 ; BCD = 9 mov bl, 9 mul bl ; * 9 ============ ax = 0051H ; BCD = 01
AX ist zwar binär korrekt = 81 (51H), aber dessen BCD-Entsprechung wäre 01, also falsch. Hier kommt AAM zum Zug:
mov al, 9 ; BCD = 9 mov bl, 9 mul bl ; * 9 aam ============ ax = 0801H ; BCD = 81
Jetzt ergibt 9 * 9 auch in BCD die gewünschte 81... ;)
Undokumentiert:
Synonym zu AAD weist Intel erst ab P5-Dokumentationen daraufhin, daß es auch eine Art von "AAM imm8" gibt. Auch "AAM imm8" wird von so einigen schon etwas länger verwendet... ;)
Ähnlich wie bei "AAD imm8" ist auch bei "AAM imm8" der 2. Opcode in der Realität ein Operand (hier eben der Divisor für AL). Wer also AL z.B. durch 8 statt 10 teilen möchte, benutzt einfach D4 08 statt D4 0a...
Btw, sollte sich jemand darüber wundern, wieso hier, entgegen anderslau- tenden (offiziellen) Dokumentationen, OF, SF und CF als gleich 0 aufge- führt sind: Damit schliesse ich mich den imo logischen Schlussfolgerung- en von Robert Collins an. Robert erläuterte das mal in einem seiner Ar- tikel wie folgt (sinngemässe Übersetzung):
"In den offiziellen Intel-Dokumentationen wird behauptet, daß bei Ein- satz des ¯normalen® AAM-Befehls (D4 0a) OF und CF nach der Ausführung undefiniert seien. Setzt man AAM strikt nach Intel-Dokumentationen ein, kann das allerdings nicht stimmen. Bei AAM wird ein 8bit-Wert durch einen anderen 8bit-Wert dividiert. Es kann somit nie zu einem Überlauf oder Übertrag kommen. Folglich müssen OF und CF = 0 sein."
"Desweiteren wird in offiziellen Intel-Dokumentationen angegeben, daß das SF der Operation entsprechend gesetzt oder gelöscht sein kann. Auch das kann nicht zutreffen, da eine Divison durch 10 immer einen Rest zwischen 0 und 9 ergibt. Da das Ergebnis also nie negativ ist, muss SF folglich immer = 0 sein."
Weitere Artikel von Robert können im Original (englisch) auf seiner Web- Site (siehe QLL0001) nachgelesen werden.
Exceptions:
RM (nur bei D4 00: Int 0 (DivideError)), Int 13 (Wrap) VM (nur bei D4 00: Int 0 (DivideError)), Int 13 (Wrap) PM (nur bei D4 00: #DE), #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | AAS - ASCII Adjust AL after Subtraction |? - - - ? ? * ? *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
AAS NP 3 3 4 3 8 3F
Pseudocode:
if AL < 6 then Tmp = 1 else Tmp = 0 endif
if ((AL AND 0fH) > 9) OR (AF = 1) then AL = (AL - 6) AND 0fH AH = AH - 1 - Tmp AF = 1 CF = 1 else AF = 0 CF = 0 AL = AL AND 0fH endif
Beschreibung:
Wie bei der Instruction AAA bereits beschrieben, rechnen Intel x86 CPUs zwar binär, aber nicht binär codiert (BCD, Binary coded decimal).
Subtrahiert man BCDs mittels SUB, kann es zu einem Stellenunterlauf kom- men, der zu falschen BCD-Ergebnissen führt. Beispiel:
mov ax, 0102H ; BCD = 12 sub al, 8 ; - 8 ================ ax = 01faH ; BCD = 1?
Offensichtlich ist das BCD-Ergebnis falsch. Also AAS eingesetzt:
mov ax, 0102H ; BCD = 12 sub al, 8 ; - 8 aas ================ ax = 0004H ; BCD = 04
Wow, Strike... ;)
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | ADC - Add with Carry |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
ADC AL, imm8 PU 1 1 2 3 4 14 ib ADC AX, imm16 PU 1 1 2 3 4 15 iw ADC EAX, imm32 PU 1 1 2 - - 15 id ADC r|m8, imm8 PU 1|3 1|3 2|7 3|7 4|17+EA 80 /2 ib ADC r|m16, imm16 PU 1|3 1|3 2|7 3|7 4|17+EA 81 /2 iw ADC r|m32, imm32 PU 1|3 1|3 2|7 - - 81 /2 id ADC r|m16, imm8 PU 1|3 1|3 2|7 3|7 4|17+EA 83 /2 ib ADC r|m32, imm8 PU 1|3 1|3 2|7 - - 83 /2 ib ADC r|m8, r8 PU 1|3 1|3 2|7 2|7 3|16+EA 10 /r ADC r|m16, r16 PU 1|3 1|3 2|7 2|7 3|16+EA 11 /r ADC r|m32, r32 PU 1|3 1|3 2|7 - - 11 /r ADC r8, r|m8 PU 1|2 1|2 2|6 2|7 3|9+EA 12 /r ADC r16, r|m16 PU 1|2 1|2 2|6 2|7 3|9+EA 13 /r ADC r32, r|m32 PU 1|2 1|2 2|6 - - 13 /r
Pseudocode:
DEST = DEST + SRC + CF
Beschreibung:
Addiert DEST mit SRC und dem Inhalt des CF, den das CF VOR Ausführung der Instruction hatte und legt das Ergebnis in DEST ab. Beispiel:
xor dx, dx mov ax, 0ffffH ; DX:AX = 0000:ffffH add ax, 3 adc dx, 0 =================== DX:AX = 0001:0002H
Ist DEST ein Word oder DWord und SRC ein imm8, wird SRC von der CPU vor der Addition automatisch vorzeichenbehaftet auf Word/DWord erweitert.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | ADD - Add |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
ADD AL, imm8 UV 1 1 2 3 4 04 ib ADD AX, imm16 UV 1 1 2 3 4 05 iw ADD EAX, imm32 UV 1 1 2 - - 05 id ADD r|m8, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 80 /0 ib ADD r|m16, imm16 UV 1|3 1|3 2|7 3|7 4|17+EA 81 /0 iw ADD r|m32, imm32 UV 1|3 1|3 2|7 - - 81 /0 id ADD r|m16, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 83 /0 ib ADD r|m32, imm8 UV 1|3 1|3 2|7 - - 83 /0 ib ADD r|m8, r8 UV 1|3 1|3 2|7 2|7 3|16+EA 00 /r ADD r|m16, r16 UV 1|3 1|3 2|7 2|7 3|16+EA 01 /r ADD r|m32, r32 UV 1|3 1|3 2|7 - - 01 /r ADD r8, r|m8 UV 1|2 1|2 2|6 2|7 3|9+EA 02 /r ADD r16, r|m16 UV 1|2 1|2 2|6 2|7 3|9+EA 03 /r ADD r32, r|m32 UV 1|2 1|2 2|6 - - 03 /r
Pseudocode:
DEST = DEST + SRC
Beschreibung:
Addiert DEST und SRC und legt das Ergebnis in DEST ab. Beispiel:
mov ax, 2 add ax, 5 ============ ax = 7
Ist DEST ein Word oder DWord und SRC ein imm8, wird SRC von der CPU vor der Addition automatisch vorzeichenbehaftet auf Word/DWord erweitert.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | AND - Logical AND |0 - - - * * ? * 0| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
AND AL, imm8 UV 1 1 2 3 4 24 ib AND AX, imm16 UV 1 1 2 3 4 25 iw AND EAX, imm32 UV 1 1 2 - - 25 id AND r|m8, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 80 /4 ib AND r|m16, imm16 UV 1|3 1|3 2|7 3|7 4|17+EA 81 /4 iw AND r|m32, imm32 UV 1|3 1|3 2|7 - - 81 /4 id AND r|m16, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 83 /4 ib AND r|m32, imm8 UV 1|3 1|3 2|7 - - 83 /4 ib AND r|m8, r8 UV 1|3 1|3 2|7 2|7 3|16+EA 20 /r AND r|m16, r16 UV 1|3 1|3 2|7 2|7 3|16+EA 21 /r AND r|m32, r32 UV 1|3 1|3 2|7 - - 21 /r AND r8, r|m8 UV 1|2 1|2 2|6 2|7 3|9+EA 22 /r AND r16, r|m16 UV 1|2 1|2 2|6 2|7 3|9+EA 23 /r AND r32, r|m32 UV 1|2 1|2 2|6 - - 23 /r
Pseudocode:
DEST = DEST AND SRC CF = 0 OF = 0
Beschreibung:
Mit den korrespondierenden Bits von DEST und SRC wird eine logische UND- Verknüpfung durchgeführt. Ergebnis der Verknüpfung wird in DEST abge- legt. Wahrheitstabelle AND:
0 AND 0 = 0 0 AND 1 = 0 1 AND 0 = 0 1 AND 1 = 1
AND wird üblicherweise eingesetzt, um bestimmte Bits in einem Byte, Word oder DWord auszublenden. Beispiel (Ausblenden der Bits 4..7):
mov al, 10110101B and al, 00001111B ==================== al = 00000101B
Ist DEST ein Word oder DWord und SRC ein imm8, wird SRC von der CPU vor der Addition automatisch vorzeichenbehaftet auf Word/DWord erweitert.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | ARPL - Adjust RPL Field of Selector |- - - - - * - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
ARPL r|m16, r16 NP PM= PM= PM= PM= - 63 /r 7 9 20|21 10|11
Pseudocode:
if DEST.RPL < SRC.RPL then ZF = 1 DEST.RPL = SRC.RPL else ZF = 0 endif
Beschreibung:
ARPL wird üblicherweise eingesetzt um sicherzustellen, daß eine Unter- routine eines Betriebssystems nicht mehr Rechte (höheres Privileg) be- sitzt, als das Betriebssystem selbst. Eine entsprechende Prüfung läuft dann in etwa so ab:
mov ax, [BS_Selector] arpl [Sub_Selector], ax
Sollte der Unterroutinen-Selector bei dieser Prüfung tatsächlich höher privilegiert sein (Sub_Selector.DPL < BS_Selector.RPL), forciert ARPL die Herabsetzung der Privilegstufe durch Gleichsetzung der RPL-Felder und zeigt die RPL-Änderung durch Setzen des ZF an. Sollten im Fall einer RPL-Änderung noch zusätzliche Maßnahmen erforderlich sein, kann der Pro- grammierer bei gesetztem ZF entsprechendes veranlassen, ohne selbst noch einen Vorher-/Nachher-Vergleich durchführen zu müssen.
ARPL wird von der CPU nur im PM erkannt und ausgeführt. Im RM oder VM wird Int 6 (Invalid) ausgelöst.
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | BOUND - Check Array Index against Bounds |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
BOUND r16, m16&16 NP 8º 7º 10º 13º - 62 /r BOUND r32, m32&32 NP 8º 7º 10º - - 62 /r
º Wenn der Test bestanden wird (also KEINE Auslösung von Int 05H)
Pseudocode:
if (DEST < SRC[LowerBound]) OR (DEST > SRC[UpperBound]) then INT 5 endif
Beschreibung:
BOUND dient der Unterstützung von sogenannten Range-Checks. Unter einem Range-Check versteht man eine Prüfung eines Wertes daraufhin, ob er so- wohl größer oder gleich einer Untergrenze, als auch kleiner oder gleich einer Obergrenze liegt.
Die Angabe m16&16 bedeutet, daß SRC auf eine Adresse zeigen muss, an der zuerst die 16bit-Untergrenze, dann die 16bit-Obergrenze liegt. Beispiel:
MaxUnten DW 1000 MaxOben DW 9999
mov ax, 5555 bound ax, [MaxUnten] :
Synonym bedeutet m32&32, daß SRC auf eine Adresse zeigen muss, an der zuerst die 32bit-Untergrenze, dann die 32bit-Obergrenze liegt.
Im Beispiel wäre der Range-Check erfolgreich. Wäre AX z.B. = 300, würde BOUND den Interrupt 5 (BOUND Range exceeded) auslösen. Mit einer eigenen ISR könnte der Programmierer sein Range-Checking auslagern und sich die sonst immer wiederkehrenden CMP/JB/JA-Gerüste sparen.
HINWEIS: Bei Interrupt 5 reagiert die CPU wie bei einem Fault, d.h. die Returnadresse auf dem Stack zeigt auf die verursachende BOUND- Instruction.
Exceptions:
RM Int 5 (Fail), Int 6 (Invalid), Int 13 (Wrap) VM Int 5 (Fail), Int 6 (Invalid), Int 13 (Wrap), #PF[Code], #AC[0] PM #BR, #UD, #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | BSF - Bit Scan Forward |? - - - ? * ? ? ?| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
BSF r16, r|m16 NP 6-34| 6-42| 10+3n - - 0F BC 6-35 7-43 BSF r32, r|m32 NP 6-42 6-42| 10+3n - - 0F BC 6-43 7-43
Pseudocode:
if SRC = 0 then ZF = 1 DEST = ? else ZF = 0 i = 0; while Bit[SRC:i] = 0 do Inc(i) DEST = i endwhile endif
Beschreibung:
BSF sucht vom LSB (Bit 0) bis maximal zum MSB (Bit 15 oder 31) nach dem ersten gesetzten Bit. Sind alle Bits = 0 wird ZF gesetzt, ansonsten ent- hält DEST die Position (Bitnummer 0..) des ersten gesetzten Bits. Bei- spiel:
mov ax, 00f8H ; 0000 0000 1111 1000B bsf cx, ax ================ cx = 3
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | BSR - Bit Scan Reverse |? - - - ? * ? ? ?| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
BSR r16, r|m16 NP 7-39| 6-103| 10+3n - - 0F BD 7-40 7-104 BSR r32, r|m32 NP 7-71| 6-103| 10+3n - - 0F BD 7-72 7-104
Pseudocode:
if SRC = 0 then ZF = 1 DEST = ? else ZF = 0 i = OperandSize-1 while Bit[SRC:i] = 0 do Dec(i) DEST = i endwhile endif
Beschreibung:
BSR sucht vom MSB (Bit 15 oder 31) bis maximal zum LSB (Bit 0) nach dem ersten gesetzten Bit. Sind alle Bits = 0 wird ZF gesetzt, ansonsten ent- hält DEST die Position (Bitnummer 0..) des ersten gesetzten Bits. Bei- spiel:
mov ax, 00f8H ; 0000 0000 1111 1000B bsr cx, ax ================ cx = 7
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | BSWAP - Byte Swap |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
BSWAP r32 NP 1 1 - - - 0F C8+rd
Pseudocode:
if OperandSize = 32 then tmp.Bits[0..7] = SRC.Bits[24..31] tmp.Bits[8..15] = SRC.Bits[16..23] tmp.Bits[16..23] = SRC.Bits[8..15] tmp.Bits[24..31] = SRC.Bits[0..7] SRC = tmp else SRC = ? endif
Beschreibung:
BSWAP dient zum Wechseln vom Little Endian auf Big Endian Formate und umgekehrt. Beispiel:
mov eax, 11223344H bswap eax ====================== eax = 44332211H
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap) PM #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | BT - Bit Test |- - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
BT r|m16, r16 NP 4|9 3|8 3|12 - - 0F A3 BT r|m32, r32 NP 4|9 3|8 3|12 - - 0F A3 BT r|m16, imm8 NP 4 3 3|6 - - 0F BA /4 ib BT r|m32, imm8 NP 4 3 3|6 - - 0F BA /4 ib
Pseudocode:
CF = Bit[SRC1:SRC2]
Beschreibung:
Das SRC2te Bit (rechter Operand) aus SRC1 (linker Operand) wird ins CF kopiert.
HINWEIS: SRC2 wird von der CPU vor der Ausführung mit MOD 32 verknüpft, d.h. der Wertebereich wird auf 0..31 begrenzt.
Beispiel:
mov ax, 8000H ; 1000 0000 0000 0000B clc ; CF = 0 bt ax, 15 ; CF = 1
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | BTC - Bit Test and Complement |- - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
BTC r|m16, r16 NP 7|13 6|13 6|13 - - 0F BB BTC r|m32, r32 NP 7|13 6|13 6|13 - - 0F BB BTC r|m16, imm8 NP 7|8 6|8 6|8 - - 0F BA /7 ib BTC r|m32, imm8 NP 7|8 6|8 6|8 - - 0F BA /7 ib
Pseudocode:
CF = Bit[SRC1:SRC2] Bit[SRC1:SRC2] = NOT CF
Beschreibung:
Das SRC2te Bit aus SRC1 wird ins CF kopiert. Danach wird das SRC2te Bit in SRC1 geflipped, d.h. aus 1 wird 0 und umgekehrt.
HINWEIS: SRC2 wird von der CPU vor der Ausführung mit MOD 32 verknüpft, der Wertebereich wird also auf 0..31 begrenzt.
Beispiel:
mov ax, 8000H ; AX = 1000 0000 0000 0000B clc ; CF = 0 btc ax, 15 ; AX = 0000 0000 0000 0000B, CF = 1
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | BTR - Bit Test and Reset |- - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
BTR r|m16, r16 NP 7|13 6|13 6|13 - - 0F B3 BTR r|m32, r32 NP 7|13 6|13 6|13 - - 0F B3 BTR r|m16, imm8 NP 7|8 6|8 6|8 - - 0F BA /6 ib BTR r|m32, imm8 NP 7|8 6|8 6|8 - - 0F BA /6 ib
Pseudocode:
CF = Bit[SRC1:SRC2] Bit[SRC1:SRC2] = 0
Beschreibung:
Das SRC2te Bit aus SRC1 wird ins CF kopiert. Danach wird das SRC2te Bit in SRC1 gelöscht.
HINWEIS: SRC2 wird von der CPU vor der Ausführung mit MOD 32 verknüpft, d.h. der Wertebereich wird auf 0..31 begrenzt.
Beispiel:
mov ax, 8004H ; AX = 1000 0000 0000 0000B clc ; CF = 0 btr ax, 15 ; AX = 0000 0000 0000 0000B, CF = 1
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | BTS - Bit Test and Set |- - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
BTS r|m16, r16 NP 7|13 6|13 6|13 - - 0F AB BTS r|m32, r32 NP 7|13 6|13 6|13 - - 0F AB BTS r|m16, imm8 NP 7|8 6|8 6|8 - - 0F BA /5 ib BTS r|m32, imm8 NP 7|8 6|8 6|8 - - 0F BA /5 ib
Pseudocode:
CF = Bit[SRC1:SRC2] Bit[SRC1:SRC2] = 1
Beschreibung:
Das SRC2te Bit aus SRC1 wird ins CF kopiert. Danach wird das SRC2te Bit in SRC1 gesetzt.
HINWEIS: SRC2 wird von der CPU vor der Ausführung mit MOD 32 verknüpft, d.h. der Wertebereich wird auf 0..31 begrenzt.
Beispiel:
mov ax, 8000H ; AX = 1000 0000 0000 0000B clc ; CF = 0 bts ax, 3 ; AX = 1000 0000 0000 1000B, CF = 0
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CALL - Call Procedure |T T T T T T T T T| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
T = Flags ändern sich nur, wenn der Call zu einem TS (Task Switch) führt
Befehl Pipe P5 486 386 286¦ 86 Opcode
------------------------------------------------------------------------- CALL rel16 PV 1 3 7+m 7 19 E8 cw CALL rel32 PV 1 3 7+m - - E8 cd
Near, relativ
------------------------------------------------------------------------- CALL r|m16 NP 2 5 7+m| 7|11 16| FF /2 10+m 21+EA CALL r|m32 NP 2 5 7+m| - - FF /2 10+m
Near, indirekt Register|Memory
------------------------------------------------------------------------- CALL ptr16:16 NP 4 18 17+m 13 28 9A cd PM= PM= PM= PM - 4-13 20 34+m 26 CALL ptr16:32 NP 4 18 17+m - - 9A cp PM= PM= PM= - - 4-13 20 34+m
Far, direkt
------------------------------------------------------------------------- CALL m16:16 NP 5 17 22+m 16 37+EA FF /3 PM= PM= PM= PM= - 5-14 20 38+m 29 CALL m16:32 NP 5 17 22+m - - FF /3 PM= PM= PM= - - 5-14 20 38+m
Far, indirekt
------------------------------------------------------------------------- CALL ptr16:16 NP 22 35 52+m 41 - 9A cd CALL ptr16:32 NP 22 35 52+m - - 9A cp CALL m16:16 NP 22 35 56+m 44 - FF /3 CALL m16:32 NP 22 35 56+m - - FF /3
via Gate, zu gleichem Privileg
------------------------------------------------------------------------- CALL ptr16:16 NP 44 69 86+m 82 - 9A cd CALL ptr16:32 NP 44 69 86+m - - 9A cp CALL m16:16 NP 44 69 90+m 83 - FF /3 CALL m16:32 NP 44 69 90+m - - FF /3
via Gate, zu höherem Privileg, keine Parameter
------------------------------------------------------------------------- CALL ptr16:16 NP 45+2x 77+4x 94+4x+m 86+4x - 9A cd CALL ptr16:32 NP 45+2x 77+4x 94+4x+m - - 9A cp CALL m16:16 NP 45+2x 77+4x 98+4x+m 90+4x - FF /3 CALL m16:32 NP 45+2x 77+4x 98+4x+m - - FF /3
via Gate, zu höherem Privileg, x Parameter
------------------------------------------------------------------------- CALL ptr16:16 NP 21+TS| 37+TS TS 177| - 9A cd 22+TS 182 CALL ptr16:32 NP 21+TS| 37+TS TS - - 9A cp 22+TS CALL m16:16 NP 21+TS| 37+TS TS 180| - FF /3 22+TS 185 CALL m16:32 NP 21+TS| 37+TS 5+TS - - FF /3 22+TS
Task (über TSS | über Task Gate)
-------------------------------------------------------------------------
¦ nur 286: plus 1 Taktzyklus pro Komponente des nächsten Befehls
+-----------------------------------+ TS (CALL) | Neuer Task | +---------------+-----------+-----------+-----------+ | Alter Task | 32bit TSS | 16bit TSS | V86 TSS | +---------------+-----------+-----------+-----------+ | 32bit TSS | 386: 300 | 386: 273 | 386: 217 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +---------------+-----------+-----------+-----------+ | 16bit TSS | 386: 298 | 386: 273 | 386: 217 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +---------------+-----------+-----------+-----------+ | V86 TSS | 386: ? | 386: ? | 386: ? | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +---------------+-----------+-----------+-----------+
386 via Task Gate TS = TS + 9
P5+ unaccessed Descriptor (n) TS = TS + (n * 8) Cache Miss (Minimum) TS = TS + 5 Branch misprediction TS = TS + 3
Pseudocode:
case AddrType of
rel16 PUSH IP EIP = (EIP + rel16) AND 0000ffffH
rel32 PUSH EIP EIP = EIP + rel32
r|m16 PUSH IP EIP = [r|m16] AND 0000ffffH
r|m32 PUSH EIP EIP = [r|m32]
else
if ((PE = 0) OR ((PE = 1) AND (VM = 1))) then
; Real oder Virtual Mode
case AddrType of
m16:16, ptr16:16 PUSH CS PUSH IP
m16:32, ptr16:32 PUSH CS PUSH EIP
endcase
case AddrType of
m16:??
; Far, indirekt
if AddrType = m16:16 then CS:IP = [m16:16] EIP = EIP AND 0000ffffH else CS:EIP = [m16:32] endif
ptr16:??
; Far, direkt
if AddrType = ptr16:16 then CS:IP = ptr16:16 EIP = EIP AND 0000ffffH else CS:EIP = ptr16:32 endif
endcase
else
; reiner Protected Mode (kein VM)
if IndirectCall then if not (EA_Access_In_Limits) #GP[0] endif endif
if NewCS_Sel = 0 then #GP[0] endif
if not (CS_SelIdx_In_DesTblLimits) then #GP[NewCS_Sel] endif
case Descriptor_Access_Rights of
Conforming_CS -------------
if DPL > CPL then #GP[CS_Sel] endif
if (Segment_not_present) then #NP[CS_Sel] endif
if (Return_Address_doesn't_fit_on_stack) #SS[0] endif
if (EIP_out_of_CS_Limits) then #GP[0] endif
CS = CS_Des CS = New_CS_Sel EIP = ZeroExtend(NewOffset)
if OperandSize = 16 then EIP = EIP AND 0000ffffH endif
Non_conforming_CS -----------------
if RPL > CPL then #GP[CS_Sel] endif
if DPL <> CPL then #GP[CS_Sel] endif
if (Segment_not_present) then #NP[CS_Sel] endif
if (Return_Address_doesn't_fit_on_stack) #SS[0] endif
if (EIP_out_of_CS_Limits) then #GP[0] endif
CS = CS_Des CS = New_CS_Sel CS.RPL = CPL EIP = ZeroExtend(NewOffset)
if OperandSize = 16 then EIP = EIP AND 0000ffffH endif
Call_Gate ---------
if CG.DPL < CPL then #GP[CG_Sel] endif
if CG.DPL < RPL then #GP[CG_Sel] endif
if (CG_not_present) then #NP[CG_Sel] endif
if CG_Des.CS_Sel = 0 then #GP[0] endif
if not (CG_Des.CS_SelIdx_In_DesTblLimits) then #GP[CS_Sel] endif
if Descriptor_Access_Rights <> CodeSeg then #GP[CS_Sel] endif
if Descriptor.DPL > CPL then #GP[CS_Sel] endif
if ((Non_conforming_CS) and (DPL < CPL)) then
; More privilege
Get New_SS_Sel_for_new_PL_from_TSS
if SS_Sel = 0 then #TS[0] endif
if not (SS_SelIdx_In_DesTblLimits) then #TS[SS_Sel] endif
if SS_Sel.RPL <> CS.DPL then #TS[SS_Sel] endif
if SS_Sel.DPL <> CS.DPL then #TS[SS_Sel] endif
if SS_Des.AR = non_writable_Seg #TS[SS_Sel] endif
if (SS_Seg_not_present) then #SS[SS_Sel] endif
if OperandSize=32 then
if (ParamsPlus16Byte_don't_fit_on_stack) #SS[SS_Sel] endif
if (EIP_out_of_CS_Limits) then #GP[0] endif
SS:eSP = TSS.SS:eSP CS:EIP = CG.CS:EIP
else
; OperandSize = 16
if (ParamsPlus8Byte_don't_fit_on_stack) #SS[SS_Sel] endif
if (IP_out_of_CS_Limits) then #GP[0] endif
SS:eSP = TSS.SS:eSP CS:IP = CG.CS:IP
endif
CS = CS_Des SS = SS_Des Push LongPtr of OldStack onto NewStack Get ParamCount from CallGate (MOD 32) Copy Params from OldStack onto NewStack Push ReturnAddress onto NewStack
CPL = SS_Sel.DPL CS.RPL = CPL
else
; Same privilege
if OperandSize=32 then
if (Return_Address_doesn't_fit_on_stack) ; 8 Bytes! #SS[0] endif
if (EIP_out_of_CS_Limits) then #GP[0] endif
CS:EIP = CG.CS:EIP
else
; OperandSize = 16
if (Return_Address_doesn't_fit_on_stack) ; 4 Bytes #SS[0] endif
if (IP_out_of_CS_Limits) then #GP[0] endif
CS:IP = CG.CS:IP
endif
Push ReturnAddress onto stack
CS = CS_Des CS.RPL = CPL
endif
Task_Gate ---------
if TG.DPL < CPL then #TS[TG_Sel] endif
if TG.DPL < RPL then #TS[TG_Sel] endif
if (Segment_not_present) then #NP[TG_Sel] endif
if (TG_Des.TTS_Sel_not_global) then #TS[TSS_Sel] endif
if not (TG_Des.TSS_SelIdx_In_GDTLimits) then #TS[TSS_Sel] endif
if (TSS_Des.AR = busy) then #TS[TSS_Sel] endif
if (TSS_not_present) then #NP[TSS_Sel] endif
SwitchTasks (nested) to TSS
if (IP_out_of_CS_Limits) then #TS[0] endif
Task_State_Segment ------------------
if TSS.DPL < CPL then #TS[TSS_Sel] endif
if TSS.DPL < RPL then #TS[TSS_Sel] endif
if (TSS_Des.AR = busy) then #TS[TSS_Sel] endif
if (TSS_not_present) then #NP[TSS_Sel] endif
SwitchTasks (nested) to TSS
if (IP_out_of_CS_Limits) then #TS[0] endif
else
#GP[CS_Sel]
endcase
endif
endcase
Beschreibung:
Durch die CALL-Instruction wird die über den Operanden adressierte Pro- zedur aufgerufen und ausgeführt. Nach Beendigung der Prozedur (RET in der Prozedur) wird die Ausführung an der Adresse fortgesetzt, die unmit- telbar auf die CALL-Anweisung folgt.
HINWEIS: Bei entsprechender Handhabung sind auch CALLs aus einem 32bit- in ein 16bit-Segment möglich. Voraussetzung ist das entsprech- ende Setzen eines 16bit OperandSize Prefix (s. Topic BAW0008) für den CALL (es sind ja 16bit RETs zu erwarten). Daß die CALLs innerhalb der ersten 64 KByte des 32bit-Segmentes liegen müs- sen, dürfte offensichtlich sein (sonst käme es zum IP-Wrap).
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #TS[0|SS_Sel|TG_Sel|TSS_Sel], #NP[CS_Sel|CG_Sel|TG_Sel|TSS_Sel], #SS[0|SS_Sel], #GP[0|CS_Sel|CG_Sel], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CBW - Convert Byte to Word |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CBW NP 3 3 3 2 2 98
Pseudocode:
AX = SignExtend(AL)
Beschreibung:
CBW erweitert einen 8bit-Wert in AL unter Erhaltung des Vorzeichens auf einen 16bit-Wert in AX. Beispiel:
mov al, -1 ; al = 0ffH cbw ; ax = 0ffffH = -1
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CDQ - Convert DWord to QWord |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CDQ NP 2 3 2 - - 99
Pseudocode:
if EAX < 0 then EDX = 0ffffffffH else EDX = 0 endif
Beschreibung:
CDQ erweitert einen 32bit-Wert in EAX unter Erhaltung des Vorzeichens auf einen 64bit-Wert in EDX:EAX. Beispiel:
mov eax, -2 ; eax = 0fffffffeH cdq ; edx:eax = 0ffffffffH:0fffffffeH
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CLC - Clear Carry Flag |- - - - - - - - 0| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CLC NP 2 2 2 2 2 F8
Pseudocode:
CF = 0
Beschreibung:
Löscht das CF.
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CLD - Clear Direction Flag |- 0 - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CLD NP 2 2 2 2 2 FC
Pseudocode:
DF = 0
Beschreibung:
Löscht das DF. CLD beeinflusst das Verhalten aller String-Instructions (LODS, STOS, MOVS, CMPS, SCAS, INS, OUTS). Nach einem CLD werden die betroffenen Indexregister (eSI und/oder eDI) fortan inkrementiert.
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CLI - Clear Interrupt Flag (Disable) |- - 0 - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CLI NP 7 5 3 3 2 FA
Pseudocode:
if PE = 1 then if VM = 0 then if CPL > IOPL then #GP[0] endif else if IOPL <> 3 then #GP[0] endif endif endif IF = 0
Beschreibung:
Löscht das IF. Dadurch werden alle über den PIC maskierbaren Hardware- Interrupts (IRQ0..IRQ15) solange gesperrt, bis IF wieder gesetzt wird. NMIs und Software-Interrupts funktionieren weiter.
Im RM ist CLI immer möglich, im VM nur bei IOPL3 und im PM solange CPL kleiner oder gleich IOPL ist.
Exceptions:
RM - VM #GP[0] PM #GP[0]
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CLTS - Clear Task-Switched Flag in CR0/MSW |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CLTS NP 10 7 5 2 - 0F 06
Pseudocode:
if (PE = 1) then if CPL <> 0 then #GP[0] endif endif CR0.TS = 0 (286: MSW.TS = 0)
Beschreibung:
Löscht das TS-Bit (Bit 3 in CR0 bzw. MSW). Das TS-Bit wird von der CPU bei jedem Task-Switch automatisch gesetzt. Bei gesetztem TS-Bit werden alle ESC- und WAIT-Instructions (WAIT nur wenn auch CR0.MP = 1) von der CPU abgefangen.
CLTS ist eine privilegierte Instruction und kann nur bei CPL0 ausgeführt werden.
Exceptions:
RM - VM #GP[0] PM #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CMC - Complement Carry Flag |- - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CMC NP 2 2 2 2 2 F5
Pseudocode:
CF = NOT CF
Beschreibung:
CMC flipped das CF, d.h. aus 1 wird 0 und umgekehrt.
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CMP - Compare Operands |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CMP AL, imm8 UV 1 1 2 3 4 3C ib CMP AX, imm16 UV 1 1 2 3 4 3D iw CMP EAX, imm32 UV 1 1 2 - - 3D id CMP r|m8, imm8 UV 1|2 1|2 2|5 3|6 4|10+EA 80 /7 ib CMP r|m16, imm16 UV 1|2 1|2 2|5 3|6 4|10+EA 81 /7 iw CMP r|m32, imm32 UV 1|2 1|2 2|5 - - 81 /7 id CMP r|m16, imm8 UV 1|2 1|2 2|5 3|6 4|10+EA 83 /7 ib CMP r|m32, imm8 UV 1|2 1|2 2|5 - - 83 /7 ib CMP r|m8, r8 UV 1|2 1|2 2|5 2|7 3|9+EA 38 /r CMP r|m16, r16 UV 1|2 1|2 2|5 2|7 3|9+EA 39 /r CMP r|m32, r32 UV 1|2 1|2 2|5 - - 39 /r CMP r8, r|m8 UV 1|2 1|2 2|6 2|6 3|9+EA 3A /r CMP r16, r|m16 UV 1|2 1|2 2|6 2|6 3|9+EA 3B /r CMP r32, r|m32 UV 1|2 1|2 2|6 - - 3B /r
Pseudocode:
FLAGS for (SRC1 - SignExtend(SRC2))
Beschreibung:
CMP subtrahiert CPU-intern SRC2 von SRC1 und setzt (ohne ein Ergebnis in SRC1 abzulegen) die entsprechenden Flags. Ist SRC1 ein Word oder DWord und SRC2 vom Typ imm8, wird SRC2 (ebenfalls nur CPU-intern) vorher auto- matisch vorzeichenbehaftet auf Word/DWord erweitert.
CMP wird üblicherweise im Zusammenhang mit Jcc-, sowie (ab 386+) SETcc- Instructions angewandt, um abhängig von bestimmten Flagzuständen zu ver- zweigen.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CMPS? - Compare String Operands |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CMPSB NP 5 8 10 8 22 A6 CMPSW NP 5 8 10 8 22 A7 CMPSD NP 5 8 10 - - A7 CMPS m8, m8 NP 5 8 10 8 22 A6 CMPS m16, m16 NP 5 8 10 8 22 A7 CMPS m32, m32 NP 5 8 10 - - A7
Pseudocode:
if AddrSize = 16 then SRCIdx = SI DESTIdx = DI else SRCIdx = ESI DESTIdx = EDI endif
case OperandSize of
Byte
if DF = 0 then IncDec = 1 else IncDec = -1 endif FLAGS for [byte ptr SRCIdx] - [byte ptr ES:DESTIdx]
Word
if DF = 0 then IncDec = 2 else IncDec = -2 endif FLAGS for [word ptr SRCIdx] - [word ptr ES:DESTIdx]
DWord
if DF = 0 then IncDec = 4 else IncDec = -4 endif FLAGS for [dword ptr SRCIdx] - [dword ptr ES:DESTIdx]
endcase
SRCIdx = SRCIdx + IncDec DESTIdx = DESTIdx + IncDec
Beschreibung:
CMPS? vergleicht das Byte, Word oder DWord auf das SRCIdx zeigt, mit dem Byte, Word oder DWord auf das ES:DESTIdx zeigt und setzt die FLAGS ent- sprechend. Wird CMPS? ohne Segment Prefix eingesetzt, wird automatisch DS als Standardsegment für SRCIdx benutzt (DS:eSI).
Abschließend werden in Abhängigkeit vom DF-Zustand SRCIdx und DESTIdx um 1, 2 od. 4 (Byte, Word oder DWord) in- (DF=0) bzw. dekrementiert (DF=1).
DF ist z.B. über die Instructions CLD (DF=0) bzw. STD (DF=1) steuerbar.
Im Zusammenhang mit REP? Prefixes und dem eCX-Register können auch ganze Byte-, Word- oder DWord-Strings miteinander verglichen werden.
Hinweis: Bei CMPS? weicht Intel von seiner sonst verwendeten Operanden- Reihenfolge ab, d.h. bei CMPS? wird der linke Operand als SRC und der rechte als DEST interpretiert (ist sonst umgekehrt)...
Folglich sind natürlich auch die FLAGS entsprechend zu inter- pretieren.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CMPXCHG - Compare and Exchange |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CMPXCHG r|m8, r8 NP 5|6 6|7º - - - 0F B0 /r CMPXCHG r|m16, r16 NP 5|6 6|7º - - - 0F B1 /r CMPXCHG r|m32, r32 NP 5|6 6|7º - - - 0F B1 /r
º m8, m16, m32: plus 3 Taktzyklen, wenn Accu <> DEST (nur 486)
Pseudocode:
case OperandSize of Byte Accu = AL Word Accu = AX DWord Accu = EAX endcase
if Accu = DEST then ZF = 1 DEST = SRC else ZF = 0 Accu = DEST endif
Beschreibung:
Ist Accu gleich DEST, wird SRC in DEST, sonst DEST in Accu abgelegt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CMPXCHG8B - Compare and exchange 8 Bytes |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CMPXCHG8B m64 NP 10 - - - - 0F C7 /1
Pseudocode:
if EDX:EAX = [qword ptr DEST] then ZF = 1 DEST = ECX:EBX else ZF = 0 EDX:EAX = [qword ptr DEST] endif
Beschreibung:
Ist EDX:EAX = DEST, wird [qword ptr ECX:EBX] in DEST, sonst DEST in EDX:EAX abgelegt. Die Angabe m64 bedeutet, daß DEST auf ein Quadword, also 8 aufeinanderfolgende Bytes, im Speicher zeigen muss. Die Verwen- dung eines Register führt zur Auslösung von Interrupt 6 (Invalid, #UD)..
Exceptions:
RM Int 6 (Invalid), Int 13 (Wrap) VM Int 6 (Invalid), Int 13 (Wrap), #PF[Code], #AC[0] PM #UD, #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CPUID - CPU Identification |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CPUID NP 12º 9º - - - 0F A2 14§ 14§
º bei PreLoad EAX <> 1 § bei PreLoad EAX == 1
Pseudocode:
case PreLoad(EAX) of
0:
EAX = MaxCPUIDLevel
1 CPUInfo supported (P5+, neuere 486) 2 CacheInfo supported (P6+)
EBX:EDX:ECX = OEM-String
68747541:69746e65:444d4163H = 'htuA':'itne':'DMAc' = 'AuthenticAMD' 69727943:736e4978:64616574H = 'iryC':'snIx':'daet' = 'CyrixInstead' 756e6547:49656e69:6c65746eH = 'uneG':'Ieni':'letn' = 'GenuineIntel' 4778654e:72446e65:6e657669H = 'GxeN':'rDne':'nevi' = 'NexGenDriven' 20434d55:20434d55:20434d55H = ' CMU':' CMU':' CMU' = 'UMC UMC UMC '
1:
EAX = CPUInfo (P5, neuere 486)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e|d c|b a 9 8|7 6 5 4|3 2 1 0| | | | | | | | | | | | | | reserviert (alle 0) | T | Famly | Model | StpID | +-----------------------------------+---+-------+-------+-------+
StpID = Stepping ID (CPU-spezifisch)
Model = Model (CPU-spezifisch)
Famly = Family
0100 = i486 0101 = P5[4c/5c] 0110 = PPro (P6)
T = Type (CPU-spezifisch). Für Intel:
00 = OEM-Processor 01 = Overdrive-Processor 10 = Dual-Processor (P5+) 11 = reserviert
EDX = CompatibilityFlags
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1| | | | | | | | | | | | | | | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0|f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |C| | |M| | |A| | | | | | | | | | | |M|M|P|T| | |P|C|M|P|M|T|P| |V|F| | |O|C|G|R| | |I|X|C|A|S|S|S|D|M|P| | reserviert (alle 0) |V|A|E|R|0|0|C|S|E|E|R|C|E|E|E|U| +-------------------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
FPU = FPU on-chip (P5+) VME = Virtual 86 Mode Enhancement (P6+) DE = Debugging Extensions (I/O Breakpoints) (P5+) PSE = Page Size Extensions (4 MByte Pages) (P6+) TSC = Time Stamp Counter/RDTSC Support (P5+) MSR = RDMSR/WRMSR Support (P5+) PAE = Physical Address Extensions (36bit) (P5+) MCE = Machine Check Exception (P5+) CXS = CMPXCHG8B Support (P5+) APIC = Advanced PIC on-chip (P6+) MTRR = Memory Type/Range Registers (P6+) PGE = PTE Global Bit (P6+) MCA = Machine Check Architecture Support (P6+) CMOV = CMOVcc-Instructions Support (P6+)
EBX = 0 ECX = 0
2:
(P6+)
AL = Anzahl notwendige Wiederholungen für (CPUID mit EAX=2), um alle Cache- und TBL-Deskriptoren zu erhalten (bei P6 m.W. immer = 1)
Jeder Deskriptor besteht aus einem einzelnen Byte. Gelöschtes MSB (Bit 31) in EAX, EBX, ECX und EDX zeigt an, daß das jeweilige Regis- ter gültige Informationen (Deskriptoren) enthält.
Deskriptorkodierung:
00H NULL Descriptor (unbenutzt)
01H Code TBL, 4 KByte Pages (4-way, 64 Entries) 02H Code TBL, 4 MByte Pages (4-way, 4 Entries)
03H Data TBL, 4 KByte Pages (4-way, 64 Entries) 04H Data TBL, 4 MByte Pages (4-way, 8 Entries)
06H L1-Cache (Code), 8 KBytes (4-way, 32 Byte Lines) 0aH L1-Cache (Data), 8 KBytes (2-way, 32 Byte Lines)
41H L2-Cache (Code&Data), 128 KBytes (4-way, 32 Byte Lines) 42H L2-Cache (Code&Data), 256 KBytes (4-way, 32 Byte Lines) 43H L2-Cache (Code&Data), 512 KBytes (4-way, 32 Byte Lines)
else
EAX = ? EBX = ? ECX = ? EDX = ?
endcase
Beschreibung:
CPUID dient der Ermittlung detaillierter Informationen über den aktiven Prozessor eines Rechners. Die Instruction wird ab P5 generell, sowie von 486ern neueren Datums unterstützt (m.W. alle i486er seit 09/94).
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap) PM #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CWD - Convert Word to DWord |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CWD NP 2 3 2 2 5 99
Pseudocode:
DX:AX = SignExtend(AX)
Beschreibung:
CWD erweitert einen 16bit-Wert in AX unter Erhaltung des Vorzeichens auf einen 32bit-Wert in DX:AX. Beispiel:
mov ax, -1 ; ax = ffffH cwd ; dx:ax = ffff:ffffH = -1
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | CWDE - Convert Word to DWord Extended |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
CWDE NP 3 3 3 - - 98
Pseudocode:
EAX = SignExtend(AX)
Beschreibung:
CWDE erweitert den 16bit-Wert in AX unter Erhaltung des Vorzeichens auf einen 32bit-Wert in EAX. Beispiel:
mov ax, -1 ; ax = ffffH cwde ; eax = ffffffffH = -1
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | DAA - Decimal Adjust AL after Addition |? - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
DAA NP 3 2 4 3 4 27
Pseudocode:
if ((AL AND 0fH) > 9) OR (AF = 1) then AL = AL + 6 AF = 1 endif
if (AL > 99H) OR (CF = 1) then AL = AL + 60H CF = 1 endif
Beschreibung:
Was AAA für BCDs ist, ist DAA für gepackte BCDs. Auch beim Addieren von gepackten BCDs kann es zu einem Stellenüberlauf kommen, der zu falschen Ergebnissen führen würde. Beispiel:
mov al, 12H ; packed BCD = 12 add al, 09H ; + packed BCD = 09 ============= al = 1bH ; = packed BCD = 1?
1bH ist offensichtlich falsch. Zur Justierung wird nun DAA eingesetzt:
mov al, 12H ; packed BCD = 12 add al, 09H ; + packed BCD = 09 daa ============= al = 21H ; = packed BCD = 21
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | DAS - Decimal Adjust AL after Subtraction |? - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
DAS NP 3 2 4 3 4 2F
Pseudocode:
if ((AL AND 0fH) > 9) OR (AF = 1) then AL = AL - 6 AF = 1 endif
if (AL > 99H) OR (CF = 1) then AL = AL - 60H CF = 1 endif
Beschreibung:
DAS ist für gepackte BCDs das gleiche, wie AAS für ungepackte BCDs. Auch beim Subtrahieren von gepackten BCDs kann es zu ergebnisverfälschenden Stellenunterläufen kommen. Beispiel:
mov al, 22H ; packed BCD = 22 sub al, 04H ; - packed BCD = 04 ============= al = 1eH ; = packed BCD = 1?
Daß 1eH keine korrekte gepackte BCD ist, liegt klar auf der Hand. Zur Justierung wird DAS verwendet:
mov al, 22H ; packed BCD = 22 sub al, 04H ; - packed BCD = 04 das ============= al = 18H ; = packed BCD = 18
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | DEC - Decrement by 1 |* - - - * * * * -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
DEC r|m8 UV 1|3 1|3 2|6 2|7 3|15+EA FE /1 DEC r|m16 UV 1|3 1|3 2|6 2|7 3|15+EA FF /1 DEC r|m32 UV 1|3 1|3 2|6 - - FF /1 DEC r16 UV 1 1 2 2 3 48+rw DEC r32 UV 1 1 2 - - 48+rd
Pseudocode:
DEST = DEST - 1
Beschreibung:
DEC vermindert DEST um 1.
Hinweis: Im Gegensatz zu den meisten anderen CPUs wird auf x86-Serien durch DEC das CF NICHT beeinflusst. Grüsse an alle ehemaligen 65xx-Progger... ;)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | DIV - Divide (unsigned) |? - - - ? ? ? ? ?| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
DIV r|m8 NP 17 16 14|17 - - F6 /6 DIV r|m16 NP 25 24 22|25 - - F7 /6 DIV r|m32 NP 41 40 38|41 - - F7 /6
Pseudocode:
case Operand of Byte Dividend = AX QReg = AL RReg = AH Word Dividend = DX:AX QReg = AX RReg = DX DWord Dividend = EDX:EAX QReg = EAX RReg = EDX endcase
Divisor = r|m if Divisor = 0 then INT 0 endif
TMP = Dividend / Divisor
if (TMP_doesn't_fit_in_quotient_register) then INT 0 else QReg = TMP RReg = Dividend MOD Operand endif
Beschreibung:
DIV vollzieht eine Division ohne Beachtung des Vorzeichens (Unsigneds). Die Register für Dividend, Quotient (QReg) und Rest (RReg) sind (wie im Pseudocode ersichtlich) durch die Operandengröße vorbestimmt:
AX / r|m8 -> AL = Quotient, AH = Rest DX:AX / r|m16 -> AX = Quotient, DX = Rest EDX:EAX / r|m32 -> EAX = Quotient, EDX = Rest
Beispiel:
xor dx, dx mov ax, 10000 ; Dividend (DX:AX) = 10000 mov bx, 10 ; Divisor = 10 div bx =============== ax = 1000 ; Quotient dx = 0 ; Rest
Exceptions:
RM Int 0 (DivideError), Int 13 (Wrap) VM Int 0 (DivideError), Int 13 (Wrap), #PF[Code], #AC[0] PM #DE, #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | ENTER - Make Stack Frame |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
ENTER imm16, 0 NP 11 14 10 11 - C8 iw 00 ENTER imm16, 1 NP 15 17 12 15 - C8 iw 01 ENTER imm16, imm8 NP 15+ 17+ 15+ 12+ - C8 iw ib 2n 3n 4(n-1) 4(n-1)
Pseudocode:
FrameSizeInBytes = LeftOperand NestingLevel = RightOperand
NestingLevel = NestingLevel MOD 32
if OperandSize = 16 then PUSH BP FramePtr = SP else PUSH EBP FramePtr = ESP endif
if NestingLevel > 0 then for i = 1 to (NestingLevel-1) if OperandSize = 16 then BP = BP - 2 PUSH [BP] else EBP = EBP - 4 PUSH [EBP] endif endfor PUSH FramePtr endif
if OperandSize = 16 then BP = FramePtr else EBP = FramePtr endif
if StackAddrSize = 16 then SP = SP - FrameSizeInBytes else ESP = ESP - ZeroExtended(FrameSizeInBytes) endif
Beschreibung:
ENTER wird zusammen mit LEAVE von vielen Hochsprachen zur Verwaltung von lokalen Variablen innerhalb einer Prozedur verwendet. ENTER erzeugt ein den Parametern entsprechendes Stackframe.
Beispiel: Angenommen man benötigt in einer seiner Prozeduren zwei lokale Variablen vom Typ Byte und eine vom Typ Word (RM, Stacksize 16bit):
PROC MyProc_
{0} SP = ff80H, BP = 9988H (Beispielwerte)
{1} enter 4, 0 {2} mov [byte ptr bp-1], 11H {3} mov [byte ptr bp-2], 22H {4} mov [word ptr bp-4], 3344H : {5} leave ret
ENDP MyProc_
Der Stack sähe nach den Einzelschritten wie folgt aus (Position des SP mit 's', des BP mit 'b' gekennzeichnet):
{0} {1} {2} {3} {4} {5} : : : : : : : : : : : : +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ ss:ff7aH + + s + s + s + s 33H + + 33H + +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ ss:ff7bH + + + + + + + + + 44H + + 44H + +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ ss:ff7cH + + + + + + + 22H + + 22H + + 22H + +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ ss:ff7dH + + + + + 11H + + 11H + + 11H + + 11H + +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ ss:ff7eH + + + 88H b + 88H b + 88H b + 88H b + 88H + +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ ss:ff7fH + + + 99H + + 99H + + 99H + + 99H + + 99H + +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ ss:ff80H s + + + + + + + + + s + +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ : : : : : : : : : : : :
Exceptions:
RM - VM - PM #SS[0], #PF[Code]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | ESC - Escape to coprocessor |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
s. BAW0011 NP ? ? ? ? ? 11011???b
Pseudocode:
if ((Opcode AND 11111000b) = 11011000b) then
if (No_Signal_at_ERROR#_Pin) then
if CR0.EM = 0 then
; Coprocessor
if CR0.TS = 1 then
; Task switched
INT 7
endif
Send_Opcode_to_FPU
else
; Emulation
INT 7
endif
else
; Coprocessor Error
INT 16
endif
endif
Beschreibung:
Opcodes, deren Bits 7..3 = 11011b sind (Opcodes D8h bis DFh, sogenannte ¯ESC-Opcodes®), werden von der CPU als Einleitung eines FPU-Befehles interpretiert und dementsprechend an die FPU (falls vorhanden) weiterge- leitet.
Auf die CPU selbst wirkt die Instruction ähnlich wie ein NOP. Es werden weder Register noch Flags verändert, sondern nur Zeit verbraucht (hier natürlich die Zeit zur Übertragung an die FPU, keine Wartezeit).
Bei normalem, fehlerfreiem Ablauf (ohne Exceptionauslösung) kann die CPU unmittelbar nach der Befehlsübermittlung an die FPU weiterarbeiten. Sie ist nicht verpflichtet, auf das Ausführungsende des FPU-Befehles zu war- ten, kann es aber natürlich, falls gewünscht (siehe WAIT-Instruction).
Detaillierte Erläuterungen zu allen FPU-Befehlen (ESC-Opcodes) sind im Topic BAW0011 (FPU-Befehlssatz) enthalten.
Exceptions:
RM Int 7 (NoCopro), Int 13 (Wrap), Int 16 (CoproErr) VM Int 7 (NoCopro), Int 13 (Wrap), Int 16 (CoproErr), #PF[Code] PM #NM, #GP[0], #PF[Code], #MF
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | HLT - Halt |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
HLT -- 4 4 5 2 2 F4
Pseudocode:
Enter_Halt_State
Beschreibung:
HLT stoppt die Ausführung von Instructions und versetzt die CPU in den sogenannten ¯Halt-State®. In diesem Modus wartet die CPU solange, bis entweder ein Reset (Soft/Hard), ein Hardware-IRQ oder der NMI ausgelöst wird.
Der Instruction Pointer (eIP) wird bei Ausführung von HLT auf die dem HLT-Opcode unmittelbar folgende Adresse gesetzt. An dieser Adresse nimmt die CPU ihre Arbeit wieder auf, jedenfalls soweit das Wecken durch einen Hardware-IRQ oder NMI und nicht durch einen Reset erfolgt.
HLT ist eine privilegierte Instruction und kann nur bei CPL0 ausgeführt werden.
Exceptions:
RM - VM #GP[0] (wenn <> CPL0) PM #GP[0] (wenn <> CPL0)
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | IDIV - Integer Divide (signed) |? - - - ? ? ? ? ?| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
IDIV r|m8 NP 22 19|20 19 17|20 101-112| F6 /6 107-118+ EA IDIV r|m16 NP 30 27|28 27 25|28 165-184| F7 /6 171-190+ EA IDIV r|m32 NP 46 43 43 - - F7 /6
Pseudocode:
case Operand of Byte Dividend = AX QReg = AL RReg = AH Word Dividend = DX:AX QReg = AX RReg = DX DWord Dividend = EDX:EAX QReg = EAX RReg = EDX endcase
Divisor = r|m if Divisor = 0 then INT 0 endif
TMP = Dividend / Divisor
if (TMP_doesn't_fit_in_quotient_register) then INT 0 else QReg = TMP RReg = Dividend MOD Operand endif
Beschreibung:
IDIV vollzieht eine Division unter Beachtung des Vorzeichens (Signeds). Die Register für Dividend, Quotient (QReg) und Rest (RReg) sind (wie im Pseudocode ersichtlich) durch die Operandengröße vorbestimmt:
AX / r|m8 -> AL = Quotient, AH = Rest DX:AX / r|m16 -> AX = Quotient, DX = Rest EDX:EAX / r|m32 -> EAX = Quotient, EDX = Rest
Beispiel:
mov ax, 16 ; Dividend = 16 mov bl, 5 ; Divisor = 5 idiv bl =============== al = 3 ; Quotient ah = 1 ; Rest
Exceptions:
RM Int 0 (DivideError), Int 13 (Wrap) VM Int 0 (DivideError), Int 13 (Wrap), #PF[Code], #AC[0] PM #DE, #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | IMUL - Integer Multiply (signed) |* - - - ? ? ? ? *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
-------------------------------------------------------------------------- IMUL r|m8 NP 11 13-18 9-14| 13|16 80-98| F6 /5 12-17 86-104+ EA AX = AL * r|m8
-------------------------------------------------------------------------- IMUL r|m16 NP 11 13-26 9-22| 21|24 128-154| F7 /5 12-25 134-160+ EA DX:AX = AX * r|m16
-------------------------------------------------------------------------- IMUL r|m32 NP 10 13-42 9-38| - - F7 /5 12-41
EDX:EAX = EAX * r|m32
-------------------------------------------------------------------------- IMUL r16, r|m16 NP 10 13-26 9-22| - - 0F AF /r 12-25
r16 = r16 * r|m16
-------------------------------------------------------------------------- IMUL r32, r|m32 NP 10 13-42 9-38| - - 0F AF /r 12-41
r32 = r32 * r|m32
-------------------------------------------------------------------------- IMUL r16, r|m16, imm8 NP 10 13-26 9-14| 21|24 - 6B /r ib 12-17
r16 = r|m16 * SignExtend(imm8)
-------------------------------------------------------------------------- IMUL r32, r|m32, imm8 NP 10 13-42 9-14| - - 6B /r ib 12-17
r32 = r|m32 * SignExtend(imm8)
-------------------------------------------------------------------------- IMUL r16, imm8 NP 10 13-26 9-14| 21|24 - 6B /r ib 12-17
r16 = r16 * SignExtend(imm8)
-------------------------------------------------------------------------- IMUL r32, imm8 NP 10 13-42 9-14| - - 6B /r ib 12-17
r32 = r32 * SignExtend(imm8)
-------------------------------------------------------------------------- IMUL r16, r|m16, imm16 NP 10 13-26 9-22| 21|24 - 69 /r iw 12-25
r16 = r|m16 * imm16
-------------------------------------------------------------------------- IMUL r32, r|m32, imm32 NP 10 13-42 9-38| - - 69 /r id 12-41
r32 = r|m32 * imm32
-------------------------------------------------------------------------- IMUL r16, imm16 NP 10 13-26 9-22| - - 69 /r iw 12-25
r16 = r16 * imm16
-------------------------------------------------------------------------- IMUL r32, imm32 NP 10 13-42 9-38| - - 69 /r id 12-41
r32 = r32 * imm32
--------------------------------------------------------------------------
Pseudocode:
Result = Multiplicand * Multiplier
Beschreibung:
IMUL führt eine Multiplikation unter Beachtung des Vorzeichens (Signeds) durch. Welche Angabe dabei als Resultat, Multiplikant und Multiplikator interpretiert wird, kann der obenstehenden Tabelle entnommen werden.
Beispiel:
mov al, 3 ; Multiplikant mov bl, 5 ; Multiplikator imul bl =============== ax = 0fH = 15 ; Produkt
Bei Verwendung nachstehender Befehlsformen werden unter den zutreffenden Bedingungen sowohl OF als auch CF gelöscht (ansonsten beide gesetzt):
Form Bedingung
IMUL r|m8 AX = SignExtend(AL) IMUL r|m16 EAX = SignExtend(AX) IMUL r|m32 EDX:EAX = SignExtend(EAX)
IMUL r16, r|m16 Ergebnis passt vollständig in r16 IMUL r16, r|m16, imm16 Ergebnis passt vollständig in r16
IMUL r32, r|m32 Ergebnis passt vollständig in r32 IMUL r32, r|m32, imm32 Ergebnis passt vollständig in r32
HINWEIS: Bei Einsatz der Accu-Varianten von IMUL (die ersten drei in der vorstehenden Tabelle) bedeutet ein gesetztes OF nicht, daß das Ergebnis unbrauchbar wäre. Die Ergebnis-Register (AX, DX:AX und EDX:EAX) sind immer groß genug, um auch das jeweils größtmög- liche Produkt der entsprechenden MaxMultiplikation aufzunehmen.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | IN - Input from Port |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
IN AL, imm8 NP 7 14 12 5 10 E4 ib PM= PM= PM= 4º,21§ 8º,28§ 6º,26§ VM=19 VM=27 VM=26 IN AX, imm8 NP 7 14 12 5 10 E5 ib PM= PM= PM= 4º,21§ 8º,28§ 6º,26§ VM=19 VM=27 VM=26 IN EAX, imm8 NP 7 14 12 - - E5 ib PM= PM= PM= 4º,21§ 8º,28§ 6º,26§ VM=19 VM=27 VM=26 IN AL, DX NP 7 14 13 5 8 EC PM= PM= PM= 4º,21§ 8º,28§ 7º,27§ VM=19 VM=27 VM=27 IN AX, DX NP 7 14 13 5 8 ED PM= PM= PM= 4º,21§ 8º,28§ 7º,27§ VM=19 VM=27 VM=27 IN EAX, DX NP 7 14 13 - - ED PM= PM= PM= 4º,21§ 8º,28§ 7º,27§ VM=19 VM=27 VM=27
º alle: wenn CPL ó IOPL
§ 386 : wenn CPL > IOPL 486+: wenn CPL ò IOPL
Pseudocode:
if (PE = 1) AND ((VM = 1) OR (CPL > IOPL)) then if NOT IO_Permission(SRC) then #GP[0] endif endif
DEST = [SRC]
Beschreibung:
IN liest den Port an Adresse SRC aus und legt den Wert in DEST ab. Port- adressen im Bereich 0..255 können direkt (imm8), höherliegende Ports nur via DX adressiert werden.
HINWEIS: Bei Verwendung der DX-Adressierung ist die tatsächlich nutzbare Datenbreite nicht automatisch auch gleich 16bit. Werden z.B. Adapterkarten adressiert, die sich in einem ISA-Slot befinden, reduziert sich die tatsächlich ausgewertete Datenbreite auf die Bits 0..9, d.h. den Adressbereich 0..1023 (000..3ffH).
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #GP[0], #PF[Code] PM #SS[0], #GP[0], #PF[Code]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | INC - Increment by 1 |* - - - * * * * -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
INC r|m8 UV 1|3 1|3 2|6 2|7 3|15+EA FE /0 INC r|m16 UV 1|3 1|3 2|6 2|7 3|15+EA FF /0 INC r|m32 UV 1|3 1|3 2|6 - - FF /0 INC r16 UV 1 1 2 2 3 40+rw INC r32 UV 1 1 2 - - 40+rd
Pseudocode:
DEST = DEST + 1
Beschreibung:
INC erhöht DEST um 1.
Hinweis: Ähnlich wie bei DEC wird auch bei INC das CF NICHT beeinflusst. Nochmal Grüße an alle ehemaligen 65xx-Progger... ;)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | INS? - Input String from Port |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
INSB NP 9 17 15 5 - 6C PM= PM= PM= 6º,24§ 10º,32§ 9º,29§ VM=22 VM=30 VM=29 INSW NP 9 17 15 5 - 6D PM= PM= PM= 6º,24§ 10º,32§ 9º,29§ VM=22 VM=30 VM=29 INSD NP 9 17 15 - - 6D PM= PM= PM= 6º,24§ 10º,32§ 9º,29§ VM=22 VM=30 VM=29 INS r|m8, DX NP 9 17 15 5 - 6C PM= PM= PM= 6º,24§ 10º,32§ 9º,29§ VM=22 VM=30 VM=29 INS r|m16, DX NP 9 17 15 5 - 6D PM= PM= PM= 6º,24§ 10º,32§ 9º,29§ VM=22 VM=30 VM=29 INS r|m32, DX NP 9 17 15 - - 6D PM= PM= PM= 6º,24§ 10º,32§ 9º,29§ VM=22 VM=30 VM=29
º alle: wenn CPL ó IOPL
§ 386 : wenn CPL > IOPL 486+: wenn CPL ò IOPL
Pseudocode:
if AddrSize = 16 then DESTIdx = DI else DESTIdx = EDI endif
if (PE = 1) AND ((VM = 1) OR (CPL > IOPL)) then if NOT IO_Permission(SRC) then #GP[0] endif endif
case OperandSize of
Byte
if DF = 0 then IncDec = 1 else IncDec = -1 endif ES:DESTIdx = INSB[Port(DX)]
Word
if DF = 0 then IncDec = 2 else IncDec = -2 endif ES:DESTIdx = INSW[Port(DX)]
DWord
if DF = 0 then IncDec = 4 else IncDec = -4 endif ES:DESTIdx = INSD[Port(DX)]
endcase
DESTIdx = DESTIdx + IncDec
Beschreibung:
INS? liest ein Byte, Word oder DWord von Port DX und speichert es an der Adresse ES:DESTIdx ab (kein Segment Override möglich).
Abschließend wird in Abhängigkeit vom DF-Zustand DESTIdx um 1, 2 oder 4 (Byte, Word oder DWord) in- (DF=0) bzw. dekrementiert (DF=1).
DF ist z.B. über die Instructions CLD (DF=0) bzw. STD (DF=1) steuerbar.
Im Zusammenhang mit REP? Prefixes und dem eCX-Register können auch mehr- ere Bytes, Words oder DWords aus dem spezifizierten Port ausgelesen und hintereinander im Speicher abgelegt werden.
HINWEIS: DX bedeutet normalerweise eine Datenbreite von 16bit. Ob diese 16bit tatsächlich in voller Breite benutzt werden können, hängt aber vom verwendeten Bussystem ab. Der ISA-Bus wertet z.B. nur 10bit-Portadressen (000..3ffH) aus.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #GP[0], #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | INT n - Call Software Interrupt Procedure |T T 0 0 T T T T T| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
T = Flags ändern sich nur, wenn der Call zu einem Task Switch führt 0 = Flags werden nur im Real Address Mode gelöscht
Befehl Pipe P5 486 386 286¦ 86 Opcode
------------------------------------------------------------------------- INT imm8 NP 16 30 37 23+m 51 CD ib INT 3 NP 13 26 33 23+m 52 CC
------------------------------------------------------------------------- INT imm8 NP 31 44 59 40+m - CD ib INT 3 NP 27 44 59 40+m - CC
PM, gleiches Privileg
------------------------------------------------------------------------- INT imm8 NP 48 77 99 78+m - CD ib INT 3 NP 44 71 99 78+m - CC
PM, höheres Privileg
------------------------------------------------------------------------- INT imm8 NP 82 86 119 - - CD ib INT 3 NP 56 82 119 - - CC
VM zu PL0
------------------------------------------------------------------------- INT imm8 NP 23+TS 37+TS TS 167+m - CD ib INT 3 NP 19+TS 37+TS TS 167+m - CC
PM, über Task Gate
-------------------------------------------------------------------------
¦ nur 286: plus 1 Taktzyklus pro Komponente des nächsten Befehls
+-----------------------------------+ TS (INT n/INTO) | Neuer Task | +-----------------+-----------+-----------+-----------+ | Alter Task | 32bit TSS | 16bit TSS | V86 TSS | +-----------------+-----------+-----------+-----------+ | 32bit TSS | 386: 309 | 386: 282 | 386: 226 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +-----------------+-----------+-----------+-----------+ | 16bit TSS | 386: 307 | 386: 280 | 386: 224 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +-----------------+-----------+-----------+-----------+ | V86 TSS | 386: 314 | 386: 287 | 386: 231 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +-----------------+-----------+-----------+-----------+
P5+ unaccessed Descriptor (n) TS = TS + (n * 8) Cache Miss PM/VM, höheres Privileg TS = TS + 12 Cache Miss PM, gleiches Privileg TS = TS + 6 Cache Miss, andere TS = TS + 3
Pseudocode:
HINWEIS: Die folgende Beschreibung gilt für Interrupts allgemein, sprich für INT 3, INTO (INT 4), INT n, externe Interrupts sowie Excep- tions.
if PE = 0 then
; Real Address Mode
PUSHF IF = 0 TF = 0 PUSH CS PUSH IP CS = IDT[IntNo*4].Seg IP = IDT[IntNo*4].Ofs
else
if VM = 0 then
; Protected Mode
if (Int_Vec_out_of_IDT_Limits) then #GP[VecNo*8+(2+EXT)] endif
if Descriptor_Access_Rights <> Gate(Interrupt OR Trap OR Task) then #GP[VecNo*8+(2+EXT)] endif
if SoftINT then
; INT 3, INTO (INT 4), INT n
if Gate_Descriptor.DPL < CPL then #GP[VecNo*8+(2+EXT)] endif
endif
if (Gate_not_present) then #NP[VecNo*8+(2+EXT)] endif
if (Trap_Gate OR Interrupt_Gate) then
if CS_Sel = 0 then #GP[EXT] endif
if NOT (CS_Sel_In_DesTblLimits) then #GP[CS_Sel+EXT] endif
if Descriptor_Access_Rights <> CS then #GP[CS_Sel+EXT] endif
if (CS_not_present) then #GP[CS_Sel+EXT] endif
if ((Non_conforming_CS) AND (DPL < CPL)) then
; more privilege
if SS_Sel = 0 then #GP[EXT] endif
if NOT (SS_SelIdx_In_DesTblLimits) then #TS[SS_Sel+EXT] endif
if SS_Sel.RPL <> CS.DPL then #TS[SS_Sel+EXT] endif
if SS.DPL <> CS.DPL then #TS[SS_Sel+EXT] endif
if SS_Des.AR = non_writable_Seg #TS[SS_Sel+EXT] endif
if (SS_not_present) then #SS[SS_Sel+EXT] endif
if 32bit_Gate then
if (Less_than_20Bytes_free_on_stack) #SS[0] endif
if (EIP_out_of_CS_Limits) then #GP[0] endif
SS:ESP = TSS.SS:ESP CS:EIP = CG.CS:EIP
Load CS_Des Load SS_Des
PUSH [OldStack] ; 4 words (3 + 1 padded)) PUSH EFLAGS PUSH [ReturnAddr] ; 4 words (3 + 1 padded))
CPL = NewCS.DPL CS.RPL = CPL
if (Interrupt_Gate) then IF = 0 endif
TF = 0 NT = 0
else
; 16bit_Gate
if (Less_than_10Bytes_free_on_stack) #SS[0] endif
if (IP_out_of_CS_Limits) then #GP[0] endif
SS:SP = TSS.SS:SP CS:IP = CG.CS:IP
Load CS_Des Load SS_Des
PUSH [OldStack] ; 2 words PUSH FLAGS PUSH [ReturnAddr] ; 2 words
CPL = NewCS.DPL CS.RPL = CPL
if (Interrupt_Gate) then IF = 0 endif
TF = 0 NT = 0
endif
else
if ((Conforming_CS) OR (DPL = CPL)) then
; same privilege
if 32bit_Gate then
if (Interrupt_caused_by_Exception) then if (Less_than_12Bytes_free_on_stack) #SS[0] endif else if (Less_than_10Bytes_free_on_stack) #SS[0] endif endif
if (EIP_out_of_CS_Limits) then #GP[0] endif
PUSH EFLAGS PUSH [ReturnAddr] ; 4 words (3 + 1 padded))
CS:EIP = CG.CS:EIP Load CS_Des CS.RPL = CPL
if (Error_Code_Exists) then PUSH [ErrorCode] endif
if (Interrupt_Gate) then IF = 0 endif
TF = 0 NT = 0
else
; 16bit_Gate
if (Interrupt_caused_by_Exception) then if (Less_than_8Bytes_free_on_stack) #SS[0] endif else if (Less_than_6Bytes_free_on_stack) #SS[0] endif endif
if (IP_out_of_CS_Limits) then #GP[0] endif
PUSH FLAGS PUSH [ReturnAddr] ; 2 words
CS:IP = CG.CS:IP Load CS_Des CS.RPL = CPL
if (Error_Code_Exists) then PUSH [ErrorCode] endif
if (Interrupt_Gate) then IF = 0 endif
TF = 0 NT = 0
endif
else
#GP[CS_Sel+EXT]
endif
endif
else
; Task_Gate
if NOT (Global_Bit_specified) then #TS[TSS_Sel] endif
if NOT (Idx_In_GDT_Limits) then #TS[TSS_Sel] endif
if Access_Rights <> Available_TSS then #TS[TSS_Sel] endif
if (TSS_not_present) then #NP[TSS_Sel] endif
SwitchTasks (with Nesting) to TSS
if (Interrupt_caused_by_Fault_with_ErrCode) then
if (Less_than_2Bytes_free_on_stack) #SS[0] endif
PUSH [ErrCode]
endif
if (IP_out_of_CS_Limits) then #GP[0] endif
endif
else
; Virtual Mode
TmpEFLAGS = EFLAGS VM = 0 TF = 0
if (Executed_through_Interrupt_Gate) then IF = 0 endif
TmpSS = SS TmpESP = ESP SS = TSS.SS(0) ESP = TSS.ESP(0)
PUSH GS ; 2 words (1 + 1 padded) PUSH FS ; 2 words (1 + 1 padded) PUSH DS ; 2 words (1 + 1 padded) PUSH ES ; 2 words (1 + 1 padded)
GS = 0 FS = 0 DS = 0 ES = 0
PUSH [TmpSS] ; 2 words (1 + 1 padded) PUSH [TmpESP] PUSH [TmpEFLAGS] PUSH [CS] ; 2 words (1 + 1 padded) PUSH [EIP]
CS:EIP = IG.Sel:IG.Ofs
endif
Beschreibung:
Mittels INT wird ein durch Software ausgelöster Call (nicht zu verwechs- eln mit dem Call eines hardwareseitig generierten IRQ) zu einer ISR ge- neriert.
Der übergebene imm8-Wert (0..255) ist lediglich ein Indexzeiger in den Interrupt Vector Table (Real Address Mode) bzw. den Interrupt Descriptor Table (Protected Mode).
Die lineare Startadresse des Vector Tables (RM) ist im Normalfall auf die Adresse 0000:0000H festgelegt, die des Descriptor Tables (PM) dage- gen kann (beinahe) beliebig sein.
Der Vector Table (RM) besteht aus einem Array mit 256, jeweils 4 Bytes langen, Einträgen. Das erste Word eines Eintrages enthält den Offset-, das zweite Word den Segmentanteil der ISR-Adresse (16:16), zu der dann schließlich gesprungen wird.
Der Descriptor Table (PM) verwendet ein anderes Format. Es handelt sich zwar auch hier um ein Array mit 256 Einträgen, aber zum einen sind hier die Einträge 8 Bytes lang und zum anderen enthalten die Einträge keine ISR-Adresse im 16:16 Format, sondern den Descriptor eines Interrupt, ei- nes Trap oder eines Task Gates (siehe dazu auch Instruction LIDT).
Wozu die ISRs der 256 INTs im Einzelnen dienen, ist etwas ;) zu umfang- reich, um es hier detailliert aufzuführen. Die wohl umfassenste Dokumen- tation hierzu ist imo Ralf Brown's Interrupt List (s. QLL0001).
Exceptions:
RM Int 3 (Break) VM Int 3 (Break), #GP[0] (INT n, IOPL < 3) PM #BP, #TS, #NP, #SS und #GP (s. Pseudocode)
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | INTO - Interrupt on Overflow |T T 0 0 T T T T T| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
T = Flags ändern sich nur, wenn der Call zu einem Task Switch führt 0 = Flags werden nur im Real Address Mode gelöscht
Befehl Pipe P5 486 386 286¦ 86 Opcode
------------------------------------------------------------------------- INTO NP 13 28 35 24+m 53 CE
INT 4 auslösen, falls OF=1
------------------------------------------------------------------------- INTO NP 27 46 59 41+m - CE
PM, gleiches Privileg
------------------------------------------------------------------------- INTO NP 44 73 99 79+m - CE
PM, höheres Privileg
------------------------------------------------------------------------- INTO NP 56 84 119 - - CE
VM zu PL0
------------------------------------------------------------------------- INTO NP 19+TS 39+TS TS 168+m - CE
PM, über Task Gate
-------------------------------------------------------------------------
Taktzyklen für Branch not taken (OF=0): 4/3/3/3/4 (P5/486/386/286/86)
¦ nur 286: plus 1 Taktzyklus pro Komponente des nächsten Befehls
Pseudocode und TS-Timings:
siehe INT n
Beschreibung:
Ist während der Ausführung von INTO das OF gesetzt, wird ein INT 4 aus- gelöst. INTO bietet dem Programmierer somit die Möglichkeit, das Over- flow-Handling seines Programmes in eine ISR auszulagern. Dadurch kann er sich speicherintensive Gerüste von JO/JNO/JMP Instructions sparen.
Exceptions:
RM Int 4 (Overflow) VM Int 4 (Overflow), #GP[0] (INT n, IOPL < 3) PM #OF, #TS, #NP, #SS und #GP (s. Pseudocode)
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | INVD - Invalidate Cache |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
INVD NP 15 4 - - - 0F 08
Pseudocode:
Flush_Internal_Cache Send_Flush_Signal_for_external_Caches
Beschreibung:
INVD löscht alle internen CPU Cache Lines, und sendet einen speziellen Bus Cycle, der auch alle externe Cache Lines zum Löschen veranlasst.
HINWEIS: Daten, die sich noch in einem externen WB-Cache (WriteBack) be- finden, werden NICHT zurückgeschrieben. Wenn also nicht gerade Fehlersituationen emuliert werden, ist WBINVD imo vorzuziehen.
HINWEIS: INVD wartet nicht, bis die Löschung der Caches vollzogen ist...
INVD ist eine privilegierte Instruction und kann nur bei CPL0 ausgeführt werden. Desweiteren handelt es sich bei INVD um eine implementationsspe- zifische Instruction; die Funktion kann auf zukünftigen CPUs variieren.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #GP[0] PM #GP[0]
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | INVLPG - Invalidate Lookup Page (TLB Entry) |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
INVLPG mem NP 25º 12º - - - 0F 01 /7
º wenn Match
Pseudocode:
Scan_TLB_for_matching_Entries_and_invalidate_every_Match
Beschreibung:
INVLPG markiert alle Einträge im TLB als ungültig, deren PagePart dem angegebenen mem-Wert entspricht.
INVLPG ist eine privilegierte Instruction und wird daher nur bei CPL0 ausgeführt. Zudem handelt es sich bei INVLPG eine implementationsspezi- fische Instruction; die Funktion kann auf zukünftigen CPUs variieren.
Die Verwendung eines Registers (anstelle von mem) quittiert die CPU mit der Auslösung von Interrupt 6 (Invalid, #UD).
Exceptions:
RM Int 6 (Invalid), Int 13 (Wrap) VM Int 6 (Invalid), Int 13 (Wrap) PM #UD, #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | IRET - Interrupt Return |* * * * * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286¦ 86 Opcode
------------------------------------------------------------------------- IRET NP 7 15 22 17+m 32 CF PM= PM= PM= 8 38 31+m IRETD NP 7 15 22 - - CF PM= PM= 10 38
------------------------------------------------------------------------- IRET NP 27 36 82 55+m - CF IRETD NP 27 36 82 - - CF
PM, zu niedrigerem Privileg
------------------------------------------------------------------------- IRET NP 10+TS 32+TS TS 169+m - CF IRETD NP 10+TS 32+TS TS - - CF
PM, zu anderem Task (NT=1)
------------------------------------------------------------------------- IRETD NP 8 15 60 - - CF
PM, zu Virtual Mode
-------------------------------------------------------------------------
¦ nur 286: plus 1 Taktzyklus pro Komponente des nächsten Befehls
+-----------------------------------+ TS (IRET/IRETD) | Neuer Task | +-----------------+-----------+-----------+-----------+ | Alter Task | 32bit TSS | 16bit TSS | V86 TSS | +-----------------+-----------+-----------+-----------+ | 32bit TSS | 386: 275 | 386: 271 | 386: 224 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +-----------------+-----------+-----------+-----------+ | 16bit TSS | 386: 265 | 386: 232 | 386: 214 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +-----------------+-----------+-----------+-----------+ | V86 TSS | 386: ? | 386: ? | 386: ? | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +-----------------+-----------+-----------+-----------+
P5+ unaccessed Descriptor (n) TS = TS + (n * 8) Cache Miss PM/VM, höheres Privileg TS = TS + 12 Cache Miss PM, gleiches Privileg TS = TS + 6 Cache Miss, andere TS = TS + 3 Branch misprediction TS = TS + 3
Pseudocode:
HINWEIS: Die folgende Beschreibung gilt für Interrupts allgemein, sprich für INT 3, INTO (INT 4), INT n, externe Interrupts sowie Excep- tions.
if PE = 0 then
; Real Address Mode
if OperandSize = 32 then
; IRETD (32bit)
EIP = POPD() CS = POPD() AND 0000ffffH EFLAGS = POPD()
else
; IRET (16bit)
IP = POPW() CS = POPW() FLAGS = POPW()
endif
else
; Protected Mode
if VM = 1 then #GP[0] endif
if NT = 1 then
; Task_Return
if (Back_Link_TTS_Sel_not_global) then #TS[New_TSS_Sel] endif
if NOT (Back_Link_TSS_SelIdx_In_GDT_Limits) then #TS[New_TSS_Sel] endif
if (Back_Link_TSS_not_busy) then #TS[New_TSS_Sel] endif
if (Back_Link_TSS_not_present) then #NP[New_TSS_Sel] endif
SwitchTasks to Back_Link_TSS (without nesting)
Mark_abandoned_task_as_NOT_BUSY
if (EIP_out_of_CS_Limits) then #TS[0] endif
else
if Return_Flags.VM = 1 then
; Stack_Return_to_VM
if Return_CS_Sel.RPL <> 3 then #GP[Return_CS_Sel] endif
if (Top_36Bytes_on_stack_out_of_SS_Limits) then #SS[0] endif
if Return_CS_Sel = 0 then #GP[0] endif
if NOT (Return_CS_SelIdx_In_Tbl_Limits) then #GP[Return_CS_Sel] endif
if Access_Rights <> Code_Seg then #GP[Return_CS_Sel] endif
if Return_CS_Sel.DPL = 3 then #GP[Return_CS_Sel] endif
if (Return_CS_not_present) then #NP[Return_CS_Sel] endif
if Return_SS_Sel = 0 then #GP[0] endif
if NOT (Return_SS_SelIdx_In_Tbl_Limits) then #GP[Return_SS_Sel] endif
if Return_SS_Sel.RPL <> Return_CS_Sel.RPL then #GP[Return_SS_Sel] endif
if Access_Rights <> Writable_Seg then #GP[Return_SS_Sel] endif
if Return_SS_Sel.DPL <> Return_CS_Sel.RPL then #GP[Return_SS_Sel] endif
if (Return_SS_not_present) then #NP[Return_SS_Sel] endif
if (Return_EIP_out_of_Return_CS_Limits) then #GP[0] endif
EFLAGS = SS:[ESP + 8] EIP = POPD() CS = POPD() AND 0000ffffH NUL = POPD() ; throwaway old EFLAGS ES = POPD() AND 0000ffffH ; 2 words (1 + 1 throwaway) DS = POPD() AND 0000ffffH ; 2 words (1 + 1 throwaway) FS = POPD() AND 0000ffffH ; 2 words (1 + 1 throwaway) GS = POPD() AND 0000ffffH ; 2 words (1 + 1 throwaway)
if CS.RPL > CPL then TmpESP = POPD() TmpSS = POPD() AND 0000ffffH SS:ESP = TmpSS:TmpESP endif
else
; Stack_Return
if OperandSize = 32 then if (3rd_word_on_Stack_out_of_Limits) then #SS[0] endif else if (2nd_word_on_Stack_out_of_Limits) then #SS[0] endif endif
if Return_CS_Sel.RPL < CPL then #GP[Return_CS_Sel] endif
if Return_CS_Sel.RPL = CPL then
; same privilege
if OperandSize = 32 then if (Top_12Bytes_on_stack_out_of_SS_Limits) then #SS[0] endif if Return_CS_Sel_at_eSP+4 = 0 then #SS[0] endif else if (Top_6Bytes_on_stack_out_of_SS_Limits) then #SS[0] endif if Return_CS_Sel_at_eSP+2 = 0 then #SS[0] endif endif
if NOT (Return_CS_SelIdx_In_Tbl_Limits) then #GP[Return_CS_Sel] endif
if Access_Rights <> Code_Seg then #GP[Return_CS_Sel] endif
if Non_conforming_Return_CS then if Return_CS_Sel.DPL <> CPL then #GP[Return_CS_Sel] endif endif
if Conforming_Return_CS then if Return_CS_Sel.DPL > CPL then #GP[Return_CS_Sel] endif endif
if (Return_CS_not_present) then #NP[Return_CS_Sel] endif
if (Return_eIP_out_of_Return_CS_Limits) then #GP[0] endif
if OperandSize = 32 then InternalLoad CS:EIP from Stack InternalLoad CS with New_CS_Descriptor InternalLoad EFLAGS with 3rd dword from stack Inc(eSP, 12) else InternalLoad CS:IP from Stack InternalLoad CS with New_CS_Descriptor InternalLoad FLAGS with 3rd word from stack Inc(eSP, 6) endif
else
; less privilege
if OperandSize = 32 then if (Top_20Bytes_on_stack_out_of_SS_Limits) then #SS[0] endif else if (Top_10Bytes_on_stack_out_of_SS_Limits) then #SS[0] endif endif
if Return_CS_Sel = 0 then #GP[0] endif
if NOT (Return_CS_SelIdx_In_Tbl_Limits) then #GP[Return_CS_Sel] endif
if Access_Rights <> Code_Seg then #GP[Return_CS_Sel] endif
if Non_conforming_Return_CS then if Return_CS_Sel.DPL <> CPL then #GP[Return_CS_Sel] endif endif
if Conforming_Return_CS then if Return_CS_Sel.DPL > CPL then #GP[Return_CS_Sel] endif endif
if (Return_CS_not_present) then #NP[Return_CS_Sel] endif
if Return_SS_Sel = 0 then #GP[0] endif
if NOT (Return_SS_SelIdx_In_Tbl_Limits) then #GP[Return_SS_Sel] endif
if Return_SS_Sel.RPL <> Return_CS_Sel.RPL then #GP[Return_SS_Sel] endif
if Access_Rights <> Writable_Seg then #GP[Return_SS_Sel] endif
if Return_SS_Sel.DPL <> Return_CS_Sel.RPL then #GP[Return_SS_Sel] endif
if (Return_SS_not_present) then #NP[Return_SS_Sel] endif
if (Return_EIP_out_of_Return_CS_Limits) then #GP[0] endif
if OperandSize = 32 then InternalLoad CS:EIP from stack InternalLoad EFLAGS with dword at [eSP+8] else InternalLoad CS:IP from stack InternalLoad FLAGS with word at [eSP+4] endif
InternalLoad SS:eSP from stack Return_CS_Sel.RPL = CPL InternalLoad CS with New_CS_Descriptor InternalLoad SS with New_SS_Descriptor
for (ES, FS, GS and DS) do
TmpValid = true
if Seg_SelIdx_out_of_Tbl_Limits then TmpValid = false endif
if Seg_Access_Rights <> (Data_Seg OR Readable_Code_Seg) then TmpValid = false endif
if Seg_is_Data_or_Non_conforming_CS then if ((Seg_DPL < CPL) OR (Seg_DPL < RPL)) then TmpValid = false endif endif
if NOT (TmpValid) Register = 0 Clear_Valid_Flag endif
enddo
endif
endif
endif
endif
Beschreibung:
IRET wird generell zum Beenden einer ISR benutzt. Das vor der Ausführung der ISR unterbrochene Programm wird nach IRET fortgesetzt.
Im RM werden dazu lediglich die vor der ISR auf dem Stack gesicherten Register eIP, CS und eFLAGS zurückgeladen.
Im PM ist das ganze natürlich etwas umfangreicher. In erster Linie wird die Funktion hier durch den Status des NT-Flags (Nested Task) bestimmt:
NT = 0
In diesem Fall erfolgt die Rückkehr OHNE Taskswitch. Das Programm, zu dem zurückgekehrt werden soll, darf nicht höher privilegiert sein als die ISR, sonst erntet man einen General Protection Fault... ;)
Bei gleicher Privilegierung werden, ähnlich wie im RM, nur eIP, CS und eFLAGS vom Stack zurückgeladen. Ist das Programm, zu dem zurück- gekehrt werden soll, niedriger privilegiert als die ISR, werden zu- sätzlich auch das alte SS und eSP wieder hergestellt.
NT = 1
Hier erfolgt die Rückkehr per Taskswitch. Dabei wird der Task, der das IRET ausführte, auf NOT BUSY gesetzt. Wird dieser Task später erneut aufgerufen, wird die Programmausführung automatisch hinter dem IRET wieder aufgenommen.
HINWEIS: Bei IRETs aus dem PM unter CPL1..3 werden die IOPL-Bits beim Zurückladen von eFLAGS ignoriert.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #GP[0] (IOPL < 3), #PF[Code], #AC[0] PM #TS, #NP, #SS und #GP (siehe Pseudocode), #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | Jcc - Jump if Condition Code is met |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286¦ 86 Opcode
JA rel8 PV 1º 3,1 7+m,3 7,3 16,4 77 cb JA rel16|32 PV 1º 3,1 7+m,3 - - 0F 87 cw|cd JAE rel8 PV 1º 3,1 7+m,3 7,3 16,4 73 cb JAE rel16|32 PV 1º 3,1 7+m,3 - - 0F 83 cw|cd JB rel8 PV 1º 3,1 7+m,3 7,3 16,4 72 cb JB rel16|32 PV 1º 3,1 7+m,3 - - 0F 82 cw|cd JBE rel8 PV 1º 3,1 7+m,3 7,3 16,4 76 cb JBE rel16|32 PV 1º 3,1 7+m,3 - - 0F 86 cw|cd JC rel8 PV 1º 3,1 7+m,3 7,3 16,4 72 cb JC rel16|32 PV 1º 3,1 7+m,3 - - 0F 82 cw|cd JCXZ rel8 PV 6,5 3,1 9+m,5 8,4 18,6 E3 cb JE rel8 PV 1º 3,1 7+m,3 7,3 16,4 74 cb JE rel16|32 PV 1º 3,1 7+m,3 - - 0F 84 cw|cd JECXZ rel8 PV 6,5 3,1 9+m,5 - - E3 cb JG rel8 PV 1º 3,1 7+m,3 7,3 16,4 7F cb JG rel16|32 PV 1º 3,1 7+m,3 - - 0F 8F cw|cd JGE rel8 PV 1º 3,1 7+m,3 7,3 16,4 7D cb JGE rel16|32 PV 1º 3,1 7+m,3 - - 0F 8D cw|cd JL rel8 PV 1º 3,1 7+m,3 7,3 16,4 7C cb JL rel16|32 PV 1º 3,1 7+m,3 - - 0F 8C cw|cd JLE rel8 PV 1º 3,1 7+m,3 7,3 16,4 7E cb JLE rel16|32 PV 1º 3,1 7+m,3 - - 0F 8E cw|cd JNA rel8 PV 1º 3,1 7+m,3 7,3 16,4 76 cb JNA rel16|32 PV 1º 3,1 7+m,3 - - 0F 86 cw|cd JNAE rel8 PV 1º 3,1 7+m,3 7,3 16,4 72 cb JNAE rel16|32 PV 1º 3,1 7+m,3 - - 0F 82 cw|cd JNB rel8 PV 1º 3,1 7+m,3 7,3 16,4 73 cb JNB rel16|32 PV 1º 3,1 7+m,3 - - 0F 83 cw|cd JNBE rel8 PV 1º 3,1 7+m,3 7,3 16,4 77 cb JNBE rel16|32 PV 1º 3,1 7+m,3 - - 0F 87 cw|cd JNC rel8 PV 1º 3,1 7+m,3 7,3 16,4 73 cb JNC rel16|32 PV 1º 3,1 7+m,3 - - 0F 83 cw|cd JNE rel8 PV 1º 3,1 7+m,3 7,3 16,4 75 cb JNE rel16|32 PV 1º 3,1 7+m,3 - - 0F 85 cw|cd JNG rel8 PV 1º 3,1 7+m,3 7,3 16,4 7E cb JNG rel16|32 PV 1º 3,1 7+m,3 - - 0F 8E cw|cd JNGE rel8 PV 1º 3,1 7+m,3 7,3 16,4 7C cb JNGE rel16|32 PV 1º 3,1 7+m,3 - - 0F 8C cw|cd JNL rel8 PV 1º 3,1 7+m,3 7,3 16,4 7D cb JNL rel16|32 PV 1º 3,1 7+m,3 - - 0F 8D cw|cd JNLE rel8 PV 1º 3,1 7+m,3 7,3 16,4 7F cb JNLE rel16|32 PV 1º 3,1 7+m,3 - - 0F 8F cw|cd JNO rel8 PV 1º 3,1 7+m,3 7,3 16,4 71 cb JNO rel16|32 PV 1º 3,1 7+m,3 - - 0F 81 cw|cd JNP rel8 PV 1º 3,1 7+m,3 7,3 16,4 7B cb JNP rel16|32 PV 1º 3,1 7+m,3 - - 0F 8B cw|cd JNS rel8 PV 1º 3,1 7+m,3 7,3 16,4 79 cb JNS rel16|32 PV 1º 3,1 7+m,3 - - 0F 89 cw|cd JNZ rel8 PV 1º 3,1 7+m,3 7,3 16,4 75 cb JNZ rel16|32 PV 1º 3,1 7+m,3 - - 0F 85 cw|cd JO rel8 PV 1º 3,1 7+m,3 7,3 16,4 70 cb JO rel16|32 PV 1º 3,1 7+m,3 - - 0F 80 cw|cd JP rel8 PV 1º 3,1 7+m,3 7,3 16,4 7A cb JP rel16|32 PV 1º 3,1 7+m,3 - - 0F 8A cw|cd JPE rel8 PV 1º 3,1 7+m,3 7,3 16,4 7A cb JPE rel16|32 PV 1º 3,1 7+m,3 - - 0F 8A cw|cd JPO rel8 PV 1º 3,1 7+m,3 7,3 16,4 7B cb JPO rel16|32 PV 1º 3,1 7+m,3 - - 0F 8B cw|cd JS rel8 PV 1º 3,1 7+m,3 7,3 16,4 78 cb JS rel16|32 PV 1º 3,1 7+m,3 - - 0F 88 cw|cd JZ rel8 PV 1º 3,1 7+m,3 7,3 16,4 74 cb JZ rel16|32 PV 1º 3,1 7+m,3 - - 0F 84 cw|cd
1. Wert = Condition Code Match (branch taken), 2. Wert = branch not taken
º nur P5+: TZ = TZ + 3 bei Branch Misprediction
¦ nur 286: plus 1 Taktzyklus pro Komponente des nächsten Befehls
Pseudocode:
if (ConditionCode) then eIP = eIP + SignExtended(RelativeOffset) if CPU >= 386 then if OperandSize = 16 then EIP = EIP AND 0000ffffH endif if (PE=1 AND VM=0) then
; raw Protected Mode
if (EIP_not_in_CS_Limits) then #GP[0] endif
endif endif endif
Beschreibung:
Bis auf JCXZ und JECXZ prüfen alle Jcc-Instructions, ob bestimmte Bits oder Bitkombinationen im FLAGS-Register momentan gesetzt und/oder gelö- scht sind. JCXZ und JECXZ dagegen prüfen nur, ob CX bzw. ECX = 0 ist.
JCXZ und JECXZ werden meist vor Schleifen eingesetzt, um zu verhindern, daß eine Schleife überhaupt durchlaufen wird, falls CX bzw. ECX gleich 0 sein sollte.
Die anderen Jcc-Instructions werden überwiegend eingesetzt um das Ergeb- nis einer CMP-Instruction auf eine bestimmte Bedingung zu prüfen, und falls dies zutrifft, zu einer bestimmten Adresse zu verzweigen:
JA Jump if Above, (CF=0 AND ZF=0), ">", unsigned JAE Jump if Above or Equal, (CF=0), ">=", unsigned JB Jump if Below, (CF=1), "<", unsigned JBE Jump if Below or Equal, (CF=1 AND ZF=1), "<=", unsigned JC Jump if Carry, (CF=1) JCXZ Jump if CX Zero, (CX=0) JE Jump if Equal, (ZF=1), "=" JECXZ Jump if ECX Zero, (ECX=0) JG Jump if Greater, (ZF=0 AND SF=OF), ">", signed JGE Jump if Greater or Equal, (SF=OF), ">=", signed JL Jump if Less, (SF<>OF), "<", signed JLE Jump if Less or Equal, (ZF=1 AND SF<>OF), "<=", signed JNA Jump if Not Above, (CF=1 OR ZF=1), "!>", unsigned JNAE Jump if Not Above or Equal, (CF=1), "!>=", unsigned JNB Jump if Not Below, (CF=0), "!<", unsigned JNBE Jump if Not Below or Equal, (CF=0 AND ZF=0), "!<=", unsigned JNC Jump if Not Carry, (CF=0) JNE Jump if Not Equal, (ZF=0), "<>" JNG Jump if Not Greater, (ZF=1 OR SF<>OF), "!>", signed JNGE Jump if Not Greater or Equal, (SF<>OF), "!>=", signed JNL Jump if Not Less, (SF=OF), "!<", signed JNLE Jump if Not Less or Equal, (ZF=0 AND SF=OF), "!<=", signed JNO Jump if Not Overflow, (OF=0) JNP Jump if Not Parity, (PF=0) JNS Jump if Not Sign, (SF=0) JNZ Jump if Not Zero, (ZF=0), "<>" JO Jump if Overflow, (OF=1) JP Jump if Parity, (PF=1) JPE Jump if Parity Even, (PF=1) JPO Jump if Parity Odd, (PF=0) JS Jump if Sign, (SF=1) JZ Jump if Zero, (ZF=1), "="
Tritt die gewünschte Bedingung ein, wird der eIP relativ zu seiner mo- mentanen Adresse um [rel8|rel16|rel32] Bytes nach vorne oder hinten ver- schoben und die Programmausführung dort fortgesetzt (Branch taken):
Bytes zurück .. Bytes vor
rel8 -128 .. 127 rel16 -32.768 .. 32.767 rel32 -2.147.483.648 .. 2.147.483.647
Es dürfte einleuchten, daß mit relativen Sprüngen keine Segmentgrenzen überschritten werden sollten... ;)
Stimmen die Bits im FLAGS-Register nicht mit dem gewünschten Condition Code (CC) überein, wird das Programm mit der der Jcc-Instruction unmit- telbar folgenden Instruction fortgesetzt (Branch not taken).
Anzumerken bleibt noch, daß die Begriffe "Greater" und "Less" für Ver- gleiche von vorzeichenbehafteten (signed) Werten, die Begriffe "Above" und "Below" dagegen für Byte-, Word- und DWord-Vergleiche (unsigned) ge- dacht sind.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap) PM #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | JMP - Jump (unconditional) |T T T T T T T T T| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
T = Flags ändern sich nur, wenn der Jump zu einem Task Switch führt
Befehl Pipe P5 486 386 286¦ 86 Opcode
-------------------------------------------------------------------------
JMP rel8 PV 1 3 7+m 7 15 EB cb
Short
-------------------------------------------------------------------------
JMP rel16 PV 1 3 7+m 7 15 E9 cw JMP rel32 PV 1 3 7+m - - E9 cd
Near
-------------------------------------------------------------------------
JMP r|m16 NP 2 5 7+m| 7+m| 11| FF /4 10+m 11+m 18+EA JMP r|m32 NP 2 5 7+m| - - FF /4 10+m
Near, indirekt Register|Memory
-------------------------------------------------------------------------
JMP ptr16:16 NP 3 17 12+m 11 15 EA cd PM= PM= PM= PM= 3-12 19 27+m 23 JMP ptr16:32 NP 3 13 12+m - - EA cp PM= PM= PM= 3-12 18 27+m
Far, direkt
-------------------------------------------------------------------------
JMP m16:16 NP 4 13 43+m 15 - FF /5 PM= PM= PM= PM= 4-13 18 31+m 26 JMP m16:32 NP 4 13 43+m - - FF /5 PM= PM= PM= 4-13 18 31+m
Far, indirekt
-------------------------------------------------------------------------
JMP ptr16:16 NP 18 32 45+m 38 - EA cd JMP ptr16:32 NP 18 31 45+m - - EA cp JMP m16:16 NP 18 31 49+m 41 - FF /5 JMP m16:32 NP 18 31 49+m - - FF /5
Call Gate, gleiches Privileg
-------------------------------------------------------------------------
JMP ptr16:16 NP 19+TS 42+TS TS 175 - EA cd JMP ptr16:32 NP 19+TS 42+TS TS - - EA cp JMP m16:16 NP 19+TS 41+TS 5+TS 178 - FF /5 JMP m16:32 NP 19+TS 41+TS 5+TS - - FF /5
Task State Segment
-------------------------------------------------------------------------
JMP ptr16:16 NP 20+TS 43+TS TS 180 - EA cd JMP ptr16:32 NP 20+TS 43+TS TS - - EA cp JMP m16:16 NP 20+TS 42+TS 5+TS 183 - FF /5 JMP m16:32 NP 20+TS 42+TS 5+TS - - FF /5
Task Gate
-------------------------------------------------------------------------
¦ nur 286: plus 1 Taktzyklus pro Komponente des nächsten Befehls
+-----------------------------------+ TS (CALL) | Neuer Task | +---------------+-----------+-----------+-----------+ | Alter Task | 32bit TSS | 16bit TSS | V86 TSS | +---------------+-----------+-----------+-----------+ | 32bit TSS | 386: 303 | 386: 276 | 386: 220 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +---------------+-----------+-----------+-----------+ | 16bit TSS | 386: 301 | 386: 274 | 386: 218 | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +---------------+-----------+-----------+-----------+ | V86 TSS | 386: ? | 386: ? | 386: ? | | | 486: ? | 486: ? | 486: ? | | | P5 : 85 | P5 : 87 | P5 : 71 | +---------------+-----------+-----------+-----------+
386 via Task Gate TS = TS + 9
P5+ unaccessed Descriptor (n) TS = TS + (n * 8) Cache Miss (Minimum) TS = TS + 5 Branch misprediction TS = TS + 3
Pseudocode:
case AddrType of
rel8, rel16, rel32
; Relativ
eIP = eIP + SignExtended(RelativeOffset) if OperandSize = 16 then EIP = EIP AND 0000ffffH endif
r|m16, r|m32
; Near, indirekt
if OperandSize = 16 then EIP = (EIP+[r|m16]) AND 0000ffffH else EIP = [r|m32] endif
else
; Far (ptr16:16, m16:16, ptr16:32, m16:32)
if ((PE=0) OR ((PE=1) AND (VM=1))) then
; Real oder Virtual Mode
if AddrType = (m16:16 OR m16:32) then
; indirekt
if OperandSize = 16 then CS:IP = [m16:16] EIP = EIP AND 0000ffffH else CS:EIP = [m16:32] endif
else
; immediate (ptr16:16, ptr16:32)
if OperandSize = 16 then CS:IP = ptr16:16 EIP = EIP AND 0000ffffH else CS:EIP = ptr16:32 endif
endif
else
; reiner Protected Mode (kein VM)
if AddrType = (m16:16 OR m16:32) then if not (EA_Access_In_Limits) #GP[0] endif endif
if (NewCS_Sel = 0) then #GP[0] endif
if (NewCSSelIdx_out_of_DesTblLimits) then #GP[NewCS_Sel] endif
case NewCS_Descriptor_Access_Rights of
Conforming_CS -------------
if DPL > CPL then #GP[NewCS_Sel] endif
if (Segment_not_present) then #NP[NewCS_Sel] endif
if (NeweIP_out_of_NewCS_Limits) then #GP[0] endif
CS = NewCS eIP = NeweIP InternalLoad CS with New_CS_Descriptor
Non_conforming_CS -----------------
if RPL > CPL then #GP[NewCS_Sel] endif
if DPL <> CPL then #GP[NewCS_Sel] endif
if (Segment_not_present) then #NP[NewCS_Sel] endif
if (NeweIP_out_of_NewCS_Limits) then #GP[0] endif
CS = NewCS eIP = NeweIP InternalLoad CS with New_CS_Descriptor CS.RPL = CPL
Call_Gate ---------
if CG.DPL < CPL then #GP[CG_Sel] endif
if CG.DPL < RPL then #GP[CG_Sel] endif
if (CG_not_present) then #NP[CG_Sel] endif
if CG_Des.CS_Sel = 0 then #GP[0] endif
if not (CG_Des.CS_SelIdx_In_DesTblLimits) then #GP[CS_Sel] endif
if Descriptor_Access_Rights <> CodeSeg then #GP[CS_Sel] endif
if Descriptor.DPL > CPL then #GP[CS_Sel] endif
if CG_Non_conforming_CS then if CG.CS_Sel.DPL <> CPL then #GP[CG.CS_Sel] endif else if CG.CS_Sel.DPL > CPL then #GP[CG.CS_Sel] endif endif
if (CG.CS_not_present) then #NP[CG.CS_Sel] endif
if (CG.eIP_out_of_CG.CS_Limits) then #GP[0] endif
CS = [CG.CS] eIP = [CG.eIP] InternalLoad CS with New_CS_Descriptor CS.RPL = CPL
Task_Gate ---------
if TG.DPL < CPL then #GP[TG_Sel] endif
if TG.DPL < RPL then #GP[TG_Sel] endif
if (Segment_not_present) then #NP[TG_Sel] endif
if (TG_Des.TTS_Sel_not_global) then #GP[TSS_Sel] endif
if not (TSS_SelIdx_In_GDTLimits) then #GP[TSS_Sel] endif
if (TSS_Des.AR = busy) then #GP[TSS_Sel] endif
if (TSS_not_present) then #NP[TSS_Sel] endif
SwitchTasks to TSS
if (eIP_out_of_CS_Limits) then #GP[0] endif
Task_State_Segment ------------------
if TSS.DPL < CPL then #GP[TSS_Sel] endif
if TSS.DPL < RPL then #GP[TSS_Sel] endif
if (TSS_Des.AR = busy) then #GP[TSS_Sel] endif
if (TSS_not_present) then #NP[TSS_Sel] endif
SwitchTasks to TSS
if (eIP_out_of_CS_Limits) then #GP[0] endif
else
; ungültiges AR-Byte im Descriptor
#GP[NewCS_Sel]
endcase
endif
endcase
Beschreibung:
Normalerweise arbeitet die CPU Befehle sequentiell, also einen nach dem anderen ab. Mittels JMP kann der Programmierer die sequentielle Abarbei- tung übergehen und die Programmausführung an einer anderen gewünschten Adresse fortsetzen lassen.
Da der Programmierer den Programfluß durch JMPs bewußt verändert, werden natürlich weder Return-Adressen (wie bei CALLs) gesichert, noch irgend- welche Bits zuvor im FLAGS-Register (wie bei Jccs) geprüft. JMPs wirken im Prinzip wie direkte Wertzuweisungen an die Register CS und eIP, was aber auf normalem Weg (MOV IP, 1234H) bekanntlich nicht funktioniert ;)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #NP[CS_Sel|CG_Sel|TG_Sel|TSS_Sel], #GP[0|CS_Sel|CG_Sel], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LAHF - Load AH with FLAGS |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LAHF NP 2 3 2 2 4 9F
Pseudocode:
AH = SF:ZF:xx:AF:xx:PF:xx:CF
Beschreibung:
LAHF speichert das LowByte des FLAGS-Registers in AH. Diese Instruction wird in der Regel verwendet, um den FLAGS-Zustand für mehrere aufeinan- derfolgende Vergleiche oder ähnliches zwischenzuspeichern.
Dies ist z.B. sinnvoll, falls die Flags zwischen den Vergleichen beein- flußt werden, bei weiteren Vergleichen aber der Urzustand benötigt wird. Zudem handelt es sich bei LAHF auch um die schnellste Möglichkeit, die Flags kurzzeitig zwischenzuspeichern (schneller als PUSHF/POP- oder MOV- Varianten).
Häufig wird LAHF auch in Verbindung mit SAHF eingesetzt, um die unteren Flags mit einem bestimmten Wert zu initialisieren, der eine spezifische Ausgangsbasis für nachfolgenden Code garantiert, oder einfach nur, um auf einen Schlag mehrere Flags gleichzeitig setzen und/oder löschen zu können.
Exceptions:
RM - VM - PM -
PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LAR - Load Access Rights |- - - - - * - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LAR r16, r|m16 NP 8 11 15|16 14|16 - 0F 02 /r LAR r32, r|m32 NP 8 11 15|16 - - 0F 02 /r
Pseudocode:
-
Beschreibung:
LAR wird üblicherweise dazu benutzt um zu überprüfen, ob ein Zeiger auf ein gültiges Segment mit ausreichender Privilegstufe weist. Für die Prü- fung benötigt man die AccessRights aus dem entsprechenden Descriptor.
Mit LAR lässt sich im PM das zweite DWord desjenigen Descriptors laden, dessen Selector man als SRC für LAR angibt. Und in eben diesem DWord be- finden sich halt die AccessRights... ;)
LAR funktioniert allerdings nur, wenn mit der zum LAR-Zeitpunkt aktuel- len Privilegstufe (CPL bzw. RPL) überhaupt auf den Selector zugegriffen werden werden darf und es sich um einen gültigen Descriptor handelt. Eine Aus- nahme bilden hier nur Conforming Code Segments: auf solche Segmente kann von jeder Privilegstufe aus zugegriffen werden.
Einen erfolgreichen Zugriffsversuch zeigt LAR durch ZF=1, Mißerfolg ent- sprechend durch ZF=0 an.
Bei DEST = r32 wird das gesamte zweite DWord des Descriptors, bei DEST = r16 nur dessen LowWord geholt. Unabhängig davon, ob DEST 16 oder 32bit ist, blendet LAR auf jeden Fall die Bits 0..7 (dort befinden sich nor- malerweise die Bits 16..23 der Segment Base) aus. Bei DEST = r32 werden zusätzlich auch die Bits 24..31 (normal Bits 24..31 der Segment Base) ausgeblendet.
Da die Bedeutung einiger Bits im zweiten Descriptor-DWord natürlich vom Descriptor-Typ abhängt, nachfolgend kurz einmal die verschiedenen Typen:
+-------- LAR r16, r|m16 -------+ +------------------------LAR r32, r|m32 ------------------------+
Descriptor-Typ: DATA (2. DWord) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1|1|1|1|1|1 1 1 1| | | | | | | | | |f e d c b a 9 8|7|6|5|4|3 2 1 0|f|e d|c|b|a|9|8|7 6 5 4 3 2 1 0| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | von LAR | | | | | | | | | | | | | von LAR | | ausmaskiert | | | |A| | | D | | | | | | ausmaskiert | | | | | |V| | | P | | | | | | | | alle 0 |G|B|0|L|? ? ? ?|P| L |1|0|E|W|A| alle 0 | +---------------+-+-+-+-+-------+-+---+-+-+-+-+-+---------------+
A = Accessed (1: ja, 0: nein) W = Writeable (1: ja, 0: nein) E = ExpandDown (1: ja, 0: nein) DPL = Descriptor Privileg Level (0..3) P = Segment present (1: ja, 0: nein) ? = undefiniert (normal Bits 16..19 Segment Limit) AVL = Available (Bit zur freien Verwendung für den Programmierer) B = Big Segment (1: ja, 0: nein) G = Granularity (1: 4KByte [Limit 4 GByte], 0: Byte [Limit 1 MByte])
Descriptor-Typ: EXECUTABLE (2. DWord) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1|1|1|1|1|1 1 1 1| | | | | | | | | |f e d c b a 9 8|7|6|5|4|3 2 1 0|f|e d|c|b|a|9|8|7 6 5 4 3 2 1 0| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | von LAR | | | | | | | | | | | | | von LAR | | ausmaskiert | | | |A| | | D | | | | | | ausmaskiert | | | | | |V| | | P | | | | | | | | alle 0 |G|D|0|L|? ? ? ?|P| L |1|1|C|R|A| alle 0 | +---------------+-+-+-+-+-------+-+---+-+-+-+-+-+---------------+
A = Accessed (1: ja, 0: nein) R = Readable (1: ja, 0: nein) C = Conforming (1: ja, 0: nein) DPL = Descriptor Privileg Level (0..3) P = Segment present (1: ja, 0: nein) ? = undefiniert (normal Bits 16..19 Segment Limit) AVL = Available (Bit zur freien Verwendung für den Programmierer) D = Default (1: USE32, 0: USE16) G = Granularity (1: 4KByte, 0: Byte)
Descriptor-Typ: SPECIAL (2. DWord) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1|1|1|1|1|1 1 1 1| | | | | | |f e d c b a 9 8|7|6|5|4|3 2 1 0|f|e d|c|b a 9 8|7 6 5 4 3 2 1 0| | | | | | | | | | | | | | | | | | | | | | | | | | von LAR | | | | | | | | | | von LAR | | ausmaskiert | | | | | | | D | | | ausmaskiert | | | | | | | | | P | | | | | alle 0 |G|X|0|X|? ? ? ?|P| L |0| DTYPE | alle 0 | +---------------+-+-+-+-+-------+-+---+-+-------+---------------+
DTYPE = Special-Descriptor-Typ:
0001 1H 286 TSS (available) 0010 2H LDT 0011 3H 286 TSS (busy) 0100 4H Call Gate 0101 5H Task Gate 0110 6H 286 Interrupt Gate 0111 7H 286 Trap Gate 1001 9H 386 TSS (available) 1011 bH 386 TSS (busy) 1100 cH 386 Call Gate 1110 eH 386 Interrupt Gate 1111 fH 386 Trap Gate
DPL = Descriptor Privileg Level (0..3) P = Segment present (1: ja, 0: nein) ? = undefiniert (normal Bits 16..19 Segment Limit) X = reserviert G = Granularity (1: 4KByte, 0: Byte)
LAR wird von der CPU nur im PM erkannt und ausgeführt. Im RM oder VM wird Interrupt 6 (Invalid) ausgelöst.
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LDS - Load pointer to DS |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LDS r16, m16:16 NP 4 6 7 7 16+EA C5 /r PM= PM= PM= PM= 13 12 22 21 LDS r32, m16:32 NP 4 6 7 - - C5 /r PM= PM= PM= 13 12 22
nur P5+: TZ = TZ + 8 bei unaccessed Descriptor
Pseudocode:
HINWEIS: Die folgende Beschreibung ist eine Zusammenfassung für alle Instructions, die ein Segmentregister laden (LDS, LES, LFS, LGS und LSS)
case Instruction of LDS: XSeg = DS LES: XSeg = ES LFS: XSeg = FS (386+) LGS: XSeg = GS (386+) LSS: XSeg = SS endcase
if OperandSize = 16 r16 = [word ptr m16:16] XSeg = [word ptr m16:16+2] else r32 = [dword ptr m16:16] XSeg = [word ptr m16:16+4] endif
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
if XSeg = SS then
; Stack Segment
if SS_Sel = 0 then #GP[0] endif
if SS_Sel_out_of_DesTblLimits then #GP[SS_Sel] endif
if CPL <> SS_Sel.RPL then #GP[SS_Sel] endif
if SS_Des.AccessRights = not_writable then #GP[SS_Sel] endif
if CPL <> SS_Des.DPL then #GP[SS_Sel] endif
if SS_not_present then #SS[SS_Sel] endif
InternalLoad SS with Selector InternalLoad SS with Descriptor
else
; DS, ES, FS oder GS
if XSeg.Sel < 4 then
; Null Selector
Clear Descriptor.ValidBit
else
; normaler Selector
if XSeg_SelIdx_out_of_DesTblLimits then #GP[XSeg_Sel] endif
if XSeg_Des.AccessRights <> (Data OR ReadableCode) then #GP[XSeg_Sel] endif
if XSeg_Des.AccessRights = (Data OR Non_conforming_CS) then if ((CPL > XSeg_Des.DPL) OR (RPL > XSeg_Des.DPL)) then #GP[XSeg_Sel] endif endif
if XSeg_not_present then #NP[XSeg_Sel] endif
InternalLoad XSeg with Descriptor
endif
InternalLoad XSeg with Selector (incl. RPL Bits)
endif
endif
Beschreibung:
LDS lädt einen an Adresse SRC liegenden 16:16 bzw. 16:32 Pointer. Der 16 bzw. 32bit-Offset des Pointers wird nach DEST (r16 bzw. r32), das 16bit Segment bzw. der 16bit Selector, nach DS geladen.
LDS wird überwiegend zum Laden von Pointern vom Stack oder auch aus In- terruptvektoren-Tabellen eingesetzt.
LDS kann im PM mit einem NullSelector (0..3) geladen werden, ohne daß es zu einem General Protection Fault (#GP) kommt. Dazu kommt es erst, wenn später ein Zugriff auf ein solches Segment erfolgt... &)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #NP[DS_Sel], #GP[DS_Sel], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LEA - Load Effective Address |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LEA r16, m UV 1 1 2 3 2+EA 8D /r LEA r32, m UV 1 1 2 - - 8D /r
Pseudocode:
if OperandSize = 16 then if AddressSize = 16 then r16 = Addr(m) else r16 = Addr(m) AND 0000ffffH endif else if AddressSize = 16 then r32 = Addr(m) AND 0000ffffH else r32 = Addr(m) endif endif
Beschreibung:
LEA berechnet die effektive Adresse von SRC, d.h. den relativen Offset vom entsprechenden Segmentbeginn bis zur Speicheradresse SRC und legt diesen in DEST ab.
HINWEIS: Wird LEA r16, m auf ein USE32-Segment, oder LEA r32, m auf ein USE16-Segment angewandt, erhält man immer nur die unteren 16bit des Offsets... ;)
HINWEIS: Einige Assemblerpakete wandeln LEA-Instructions, die sich auf eine konstante effektive Speicheradresse beziehen, u.U. automa- tisch (i.d.R. einstellungsabhängig) in MOV-Instructions um, um den RunTime-Code zu verkürzen (MOV ist in diesem Fall meist ein Byte kürzer als LEA).
LEA wurde früher fast ausschließlich für Initialisierungen vor der Aus- führung von z.B. XLAT- oder String-Instructions (LODS, STOS, MOVS, usw.) benutzt.
Seit den 386ern "zweckentfremden" viele Programmierer LEA aber auch z.B. für mathematische Kettenoperationen, oder Adressberechnungen mit mehrer- en Komponenten, da LEA in vielen Situationen (s. dazu auch OPT0004) um ein Vielfaches schneller ist, als entsprechende andere Befehle bzw. Be- fehlssequenzen.
Übrigens, SRC sollte vom Assembler immer als Speicheradresse interpre- tiert werden können, auch wenn nur Register verwendet werden, ansonsten verabschiedet sich die CPU mit Interrupt 6 (Invalid, #UD)... &)
Exceptions:
RM Int 6 (Invalid), Int 13 (Wrap) VM Int 6 (Invalid), Int 13 (Wrap) PM #UD
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LEAVE - Release Stack Frame |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LEAVE NP 3 5 4 5 - C9
Pseudocode:
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
if eBP_out_of_Stack_Limits then #SS[0] endif
endif
if StackAddrSize = 16 then SP = BP else ESP = EBP endif
if OperandSize = 16 then BP = POPW() else EBP = POPD() endif
Beschreibung:
LEAVE wird dazu benutzt, ein zuvor eingerichtetes, lokales Stackframe wieder freizugeben, d.h. eBP und eSP wieder auf die Werte zurückzuset- zen, die sie vor der Einrichtung des Frames besaßen.
LEAVE wird zusammen mit ENTER und RET n überwiegend durch Hochsprachen verwendet, da sich mit diesen Instructions ein bequemes LowLevel Stack- bzw. Parameter-Interface realisieren lässt.
HINWEIS: Wie aus dem Pseudocode ersichtlich ist, setzt LEAVE den Stack- pointer (eSP) auf den zum LEAVE-Zeitpunkt aktuellen Inhalt des Basepointers (eBP) und POPed erst dann den ursprünglichen Base- pointer vom Stack.
Solange das lokale Stackframe via ENTER eingerichtet wurde und eBP während der Routine unangetastet blieb, sind bei LEAVE auch keine Probleme zu erwarten. Häufig ist es, speziell in der ASM- Programmierung, jedoch so, daß aus Geschwindigkeitsgründen gar kein ENTER zur Frame-Errichtung verwendet, und/oder eBP auf- grund chronischen Registermangels ;) bzw. wiederum aus Perfor- mancegründen durch die Routine "mißbraucht" wurde.
In solchen Fällen muß der Programmierer natürlich selbst dafür sorgen, daß eBP wieder den "richtigen" Wert erhält, bevor ein LEAVE ausgeführt wird, ansonsten dürfte der Guru grüßen... &)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap) PM #SS[0], #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LES - Load pointer to ES |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LES r16, m16:16 NP 4 6 7 7 16+EA C4 /r PM= PM= PM= PM= 13 12 22 21 LES r32, m16:32 NP 4 6 7 - - C4 /r PM= PM= PM= 13 12 22
nur P5+: TZ = TZ + 8 bei unaccessed Descriptor
Pseudocode:
siehe LDS
Beschreibung:
LES lädt einen an Adresse SRC liegenden 16:16 bzw. 16:32 Pointer. Der 16 bzw. 32bit-Offset des Pointers wird nach DEST (r16 bzw. r32), das 16bit Segment bzw. der 16bit Selector, nach ES geladen.
LES wird überwiegend zum Laden von Pointern vom Stack oder auch aus In- terruptvektoren-Tabellen eingesetzt.
LES kann im PM mit einem NullSelector (0..3) geladen werden, ohne daß es zu einem General Protection Fault (#GP) kommt. Dazu kommt es erst, wenn später ein Zugriff auf ein solches Segment erfolgt... &)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #NP[ES_Sel], #GP[ES_Sel], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LFS - Load pointer to FS |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LFS r16, m16:16 NP 4 6 7 - - 0F B4 /r PM= PM= PM= 13 12 22 LFS r32, m16:32 NP 4 6 7 - - 0F B4 /r PM= PM= PM= 13 12 22
nur P5+: TZ = TZ + 8 bei unaccessed Descriptor
Pseudocode:
siehe LDS
Beschreibung:
LFS lädt einen an Adresse SRC liegenden 16:16 bzw. 16:32 Pointer. Der 16 bzw. 32bit-Offset des Pointers wird nach DEST (r16 bzw. r32), das 16bit Segment bzw. der 16bit Selector, nach FS geladen.
LFS wird überwiegend zum Laden von Pointern vom Stack oder auch aus In- terruptvektoren-Tabellen eingesetzt.
LFS kann im PM mit einem NullSelector (0..3) geladen werden, ohne daß es zu einem General Protection Fault (#GP) kommt. Dazu kommt es erst, wenn später ein Zugriff auf ein solches Segment erfolgt... &)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #NP[FS_Sel], #GP[FS_Sel], #PF[Code], #AC[0]
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LGDT - Load Global Descriptor Table Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LGDT m16&32 NP 6 11 11 11 - 0F 01 /2
Pseudocode:
if ((PE = 1) AND (VM = 0)) then if CPL <> 0 then #GP[0] endif endif
if m16&32 = Type_of_register then
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
#UD[0]
else
; RM oder VM
Int 6
endif
endif
TmpBase = [dword ptr m16&32+2] if OperandSize = 16 then TmpBase = TmpBase AND 00ffffffH endif GDTR.Base = TmpBase GDTR.Limit = [word ptr m16&32]
Beschreibung:
Mit LGDT wird die Lage (genauer: Base und Limit) des Global Descriptor Tables (GDT) an das GDTR (GDT Register) übergeben. Der GDT ist nichts anderes als eine Tabelle (vorzugsweise im RAM), die aus bis zu 8192, je- weils 8 Byte langen Entries (Descriptors), besteht. Der erste Entry ent- hält einen Null Descriptor. Die CPU benutzt GDTR und die jeweiligen Des- criptors aus der GDT, um ein logische Adresse in eine lineare, und diese wiederum in eine physikalische Adresse übersetzen zu können.
HINWEIS: Die Adressierungsart m16&32 wird oft unbewußt als ptr16:32 miß- interpretiert. Bei m16&32 erwartet die CPU an Offset 0 das Word für Limit (16bit), und an Offset 2 das DWord für Base (24 bzw. 32bit (286 bzw. 386+)).
LGDT ist eine privilegierte Instruction und kann nur bei CPL0 ausgeführt werden.
LGDT wird üblicherweise zur Initialisierung von PM-Umgebungen bei Kalt- und/oder Warmstarts eingesetzt, und daher i.d.R. nur von Betriebssystem- en verwendet.
Eine der "berühmten" Ausnahmen für den Einsatz von LGDT ist die Aufheb- ung der allseits bekannten 64 KByte-Segmentgrenzen, die normalerweise ja für den Real Address Mode gelten. Bei entsprechender Vorbereitung ist es nämlich durchaus möglich, auch im RM mit bis zu 4 GByte großen Segmenten zu arbeiten... 8)
Jeder, der sich schon einmal etwas intensiver mit der Programmierung von Demos, Intros oder Games beschäftigt hat, weiß, wovon hier die Rede ist: Vom sagenumwobenen, legendenumrankten ¯RM/Flat4G-Model® (s. BAW0015). &)
Exceptions:
RM Int 6 (Invalid), Int 13 (Wrap) VM Int 6 (Invalid), Int 13 (Wrap), #PF[Code] PM #UD, #SS[0], #GP[0], #PF[Code]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LGS - Load pointer to GS |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LGS r16, m16:16 NP 4 6 7 - - 0F B5 /r PM= PM= PM= 13 12 22 LGS r32, m16:32 NP 4 6 7 - - 0F B5 /r PM= PM= PM= 13 12 22
nur P5+: TZ = TZ + 8 bei unaccessed Descriptor
Pseudocode:
siehe LDS
Beschreibung:
LGS lädt einen an Adresse SRC liegenden 16:16 bzw. 16:32 Pointer. Der 16 bzw. 32bit-Offset des Pointers wird nach DEST (r16 bzw. r32), das 16bit Segment bzw. der 16bit Selector, nach GS geladen.
LGS wird überwiegend zum Laden von Pointern vom Stack oder auch aus In- terruptvektoren-Tabellen eingesetzt.
LGS kann im PM mit einem NullSelector (0..3) geladen werden, ohne daß es zu einem General Protection Fault (#GP) kommt. Dazu kommt es erst, wenn später ein Zugriff auf ein solches Segment erfolgt... &)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #NP[GS_Sel], #GP[GS_Sel], #PF[Code], #AC[0]
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LIDT - Load Interrupt Descriptor Table Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LIDT m16&32 NP 6 11 11 12 - 0F 01 /3
Pseudocode:
if ((PE = 1) AND (VM = 0)) then if CPL <> 0 then #GP[0] endif endif
if m16&32 = Type_of_register then
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
#UD[0]
else
; RM oder VM
Int 6
endif
endif
TmpBase = [dword ptr m16&32+2] if OperandSize = 16 then TmpBase = TmpBase AND 00ffffffH endif IDTR.Base = TmpBase IDTR.Limit = [word ptr m16&32]
Beschreibung:
Mit LIDT kann man die Lage (genauer: Base und Limit) des Interrupt Des- criptor Tables (IDT) an das interne IDTR (IDT Register) übergeben.
Ähnlich wie der GDT ist auch der IDT nichts anderes als eine Tabelle, die aus jeweils 8 Byte langen Entries (Descriptors) besteht. Im Gegen- satz zum GDT enthält der IDT jedoch höchstens 256 Entries, da über den IDT nur ISRs, d.h. Programmcode für Interrupt- und Exception-Handler an- gesprungen werden, von denen es auf IBM PCs (und Kompatiblen) bekannt- lich ja nur 256 Stück gibt... ;)
HINWEIS: Die Adressierungsart m16&32 wird oft unbewußt als ptr16:32 miß- interpretiert. Bei m16&32 erwartet die CPU an Offset 0 das Word für Limit (16bit), und an Offset 2 das DWord für Base (24 bzw. 32bit (286 bzw. 386+)).
HINWEIS: In einem IDT sind nur Task, Trap und Interrupt Descriptors zu- lässig.
LIDT ist eine privilegierte Instruction und kann nur bei CPL0 ausgeführt werden.
LIDT wird üblicherweise zur Initialisierung von PM-Umgebungen bei Kalt- und/oder Warmstarts eingesetzt, und daher i.d.R. nur von Betriebssystem- en verwendet.
Exceptions:
RM Int 6 (Invalid), Int 13 (Wrap) VM Int 6 (Invalid), Int 13 (Wrap), #PF[Code] PM #UD, #SS[0], #GP[0], #PF[Code]
PRI PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LLDT - Load Local Descriptor Table Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LLDT r|m16 NP 9 11 20|24 17|19 - 0F 00 /2
Pseudocode:
LDTR = GDT.Descriptor[Selector(r|m16)]
Beschreibung:
Mit LLDT wird die Lage (genauer: Base und Limit) eines Local Descriptor Tables (LDT) an das LDTR (LDT Register) übergeben.
Ein LDT besitzt im Prinzip das gleiche Format, wie ein GDT, ist also auch nichts anderes, als eine Tabelle (vorzugsweise im RAM), die aus bis zu 8192, jeweils 8 Byte langen Entries (Descriptors), besteht.
Allerdings unterscheiden sich (L)LDT und (L)GDT in mehreren Punkten:
- Der GDT ist global (für alle Tasks), ein LDT dagegen nur lokal (für den aktuellen Task) zugänglich. Vorsicht, Stolperfalle: Auch wenn nur der aktuelle Task auf den LDT zugreifen kann, so bedeutet dies aber nicht, daß über den LDT kein globaler Datenbereich adressierbar wäre... ;)
- Der im GDT zwingend erforderliche Null-Descriptor kann in einem LDT wegfallen, er ist nicht Bedingung.
- Bei LGDT erfolgt die Parameterübergabe (Limit&Base) via Adressier- ungsart m16&32. Bei LLDT trifft das nicht zu, hier ist lediglich ein Selector (SRC) anzugeben, der auf einen LDT-Descriptor im GDT zeigt. Base, Limit und Attribute lädt die CPU dann automatisch selbst.
Zeigt der übergebene Selector auf einen LDT-Descriptor im GDT, wird LDTR entsprechend geladen. Vorsicht, wieder Stolperfalle ;): TSS.LDT und alle Segmentregister-Deskriptoren (CS, DS, ES, FS, GS und SS) werden durch diesen Vorgang nicht beeinflusst.
Sollte der angegebene Selector nicht auf einen LDT-Descriptor zeigen, so wird das LDTR intern als ungültig markiert. Bei ungültigem LDTR führen alle zukünftigen Descriptor-Referenzen (außer via LAR, LSL, VERR oder VERW) zu einer allgemeinen Schutzverletzung (#GP[0])...
LLDT wird von der CPU nur im PM erkannt und ausgeführt. Im RM oder VM wird Int 6 (Invalid) ausgelöst.
LLDT ist eine privilegierte Instruction und kann nur bei CPL0 ausgeführt werden. LLDT wird i.d.R. nur von Betriebssystemen verwendet.
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #NP[Selector], #SS[0], #GP[0|Selector], #PF[Code]
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LMSW - Load Machine Status Word |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LMSW r|m16 NP 8 13 10|13 3|6 - 0F 01 /6
Pseudocode:
MSW = r|m16
Beschreibung:
Via LMSW kann man ab 80286 CPUs das interne Register MSW (Machine Status Word, s. dazu auch BAW0007) auf einen gewünschten Wert (r|m16) setzen.
HINWEIS: Ab 80386 CPUs ist das MSW Bestandteil von CR0, d.h. bei Zugriff auf das MSW via LMSW oder SMSW werden in der Realität die Bits 0..15 von CR0 adressiert. Dem Programmierer kann diese interne Umleitung herzlich egal sein ;), LMSW und SMSW verhalten sich nach außen genau wie zu Zeiten des 80286, d.h. immer noch als unprivilegierte Instructions (im PM kein CPL0 zur Ausführung erforderlich).
HINWEIS: LMSW kann dazu benutzt werden, um die CPU vom RM in den PM zu schalten (durch MSW.PE = 1). Sollte LMSW zur Umschaltung ver- wendet werden, darf auf keinen Fall ein unmittelbar folgender PQ-Flush (Löschen des Prefetch-Queues, z.B. mittels NEAR JMP) fehlen, sonst dürfte sich die CPU aufgrund bereits vordekodier- ter bzw. prefetchter Instructions verabschieden (Hang)... ;)
Vorsicht: Dies gilt auch für P6er. LMSW gehört NICHT zu den so- genannten serialized Instructions (Instructions, die den PQ-Flush selbst ausführen, wie z.B. MOV CR0, x).
HINWEIS: Nein, LMSW kann NICHT benutzt werden, um vom PM zurück in den RM zu schalten... >-] Desweiteren lassen sich weder MSW.NE, noch MSW.ET via LMSW beeinflussen.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code] PM #SS[0], #GP[0], #PF[Code]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LOCK - Assert LOCK# Signal Prefix |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LOCK -- 1 1 0 0 2 F0
Pseudocode:
BusLock_while_following_Instruction
Beschreibung:
Das Prefix LOCK stellt man einem Befehl voran, wenn man in einer Mehr- fachprozessor-Umgebung gewährleisten möchte/muß, daß die aktuelle CPU während der nächsten Instruction alleiniges Zugriffsrecht auf einen von mehreren CPUs benutzten Speicherbereich (sogenanntes COMMON oder SHARED Memory) hat.
Das LOCK-Prefix ist ausschließlich bei folgenden Instructions zulässig:
ADC mem, reg | mem, imm ADD mem, reg | mem, imm AND mem, reg | mem, imm BTC mem, reg | mem, imm BTR mem, reg | mem, imm BTS mem, reg | mem, imm CMPXCHG mem, reg DEC mem INC mem NEG mem NOT mem OR mem, reg | mem, imm SBB mem, reg | mem, imm SUB mem, reg | mem, imm XADD mem, reg XCHGº mem, reg | reg, mem XOR mem, reg | mem, imm
º XCHG wurde nur der Vollständigkeit wegen aufgeführt. In der Praxis ist ein LOCK XCHG jedoch überflüssig, da die CPU bei XCHG automatisch ein LOCK# Signal sendet, auch wenn das LOCK-Prefix fehlt.
In Verbindung mit allen vorstehend nicht aufgeführten Instructions führt ein LOCK-Prefix immer zur Auslösung von Interrupt 6 (Invalid, #UD).
HINWEIS: Sollte eine andere CPU gleichzeitig, aber ungesichert, auf den gleichen Speicheroperanden zugreifen (sei es mit einer LOCKable Instruction ohne LOCK-Prefix, oder mit einer generell LOCK-un- fähigen Instruction, wie z.B. MOV), ist KEIN Zugriffsschutz ge- geben.
Gleiches gilt, wenn SRC und DEST nicht exakt die gleiche Größe haben (PartialOverlap, z.B. DEST = Word, SRC = Byte).
HINWEIS: Das Alignment (die Ausrichtung) des angesprochenen Speicherope- randen spielt für LOCK keine Rolle. Der Zugriffsschutz ist auch bei Misalignment erst einmal grundsätzlich gegeben.
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #UD
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LODS? - Load String Operand |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LODSB NP 2 5 5 5 12 AC LODSW NP 2 5 5 5 12 AD LODSD NP 2 5 5 - - AD LODS m8 NP 2 5 5 5 12 AC LODS m16 NP 2 5 5 5 12 AD LODS m32 NP 2 5 5 - - AD
Pseudocode:
if AddrSize = 16 then SRCIdx = SI else SRCIdx = ESI endif
case OperandSize of
Byte
if DF = 0 then IncDec = 1 else IncDec = -1 endif AL = [byte ptr SRCIdx]
Word
if DF = 0 then IncDec = 2 else IncDec = -2 endif AX = [word ptr SRCIdx]
DWord
if DF = 0 then IncDec = 4 else IncDec = -4 endif EAX = [dword ptr SRCIdx]
endcase
SRCIdx = SRCIdx + IncDec
Beschreibung:
LODS? lädt das Byte, Word oder DWord, auf das SRCIdx zeigt, nach AL, AX bzw. EAX. Wird LODS? kein Segment Prefix vorangestellt, wird automatisch DS als Standardsegment benutzt (DS:eSI).
Abschließend werden in Abhängigkeit vom DF-Zustand SRCIdx und DESTIdx um 1, 2 od. 4 (Byte, Word oder DWord) in- (DF=0) bzw. dekrementiert (DF=1).
DF ist z.B. über die Instructions CLD (DF=0) bzw. STD (DF=1) steuerbar.
Im Zusammenhang mit REP? Prefixes und dem eCX-Register können auch ganze Byte-, Word- oder DWord-Strings schrittweise geladen werden.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LOOP? - Loop Control with eCX Counter |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LOOP NP 5,6 6,2 11+m,3 8,4 17,5 E2 cb LOOPE NP 7,8 9,6 11+m,3 8,4 18,6 E1 cb LOOPZ NP 7,8 9,6 11+m,3 8,4 18,6 E1 cb LOOPNE NP 7,8 9,6 11+m,3 8,4 19,5 E0 cb LOOPNZ NP 7,8 9,6 11+m,3 8,4 19,5 E0 cb
1. Wert für Sprung (Branch taken), 2. Wert für KEIN Sprung (no branch)
Pseudocode:
eCX = eCX - 1
case Instruction of LOOP: BranchConditionMet = (eCX <> 0) LOOPE, LOOPZ: BranchConditionMet = ((ZF = 1) AND (eCX <> 0)) LOOPNE, LOOPNZ: BranchConditionMet = ((ZF = 0) AND (eCX <> 0)) endcase
if BranchConditionMet then eIP = eIP + SignExtend(rel8) endif
Beschreibung:
LOOP? ist, wie der Name bereits erahnen lässt ;), für die bequeme Pro- grammierung von Loops (englisch, Schleifen) gedacht. Loops kommen über- wiegend in Aus- und Eingabemodulen, sowie in Kopier- und Verlagerungs- vorgängen oder Zähl- und Warteschleifen zum Einsatz.
Beispiel:
mov cx, 1000 mov ax, 0
@@1: add ax, 1 loop @@1
AX = 1000
HINWEIS: So mancher Einsteiger glaubt anfänglich, daß die Zählrichtung für eCX bei LOOP? durch das DF beeinflusst wird. Natürlich ist das nicht der Fall; bei allen LOOP-Varianten wird immer automa- tisch um eins dekrementiert.
HINWEIS: Obwohl auch ein reines LOOP (Opcode E2 cb) eigentlich dazu prä- destiniert sein sollte (Grüße an die Intel Entwickler ;)), gibt es weitaus schnellere Lösungen als LOOP selbst (zumindest was 386, 486 und P5 betrifft).
Dem LOOP vorzuziehen ist imo folgende Sequenz:
386 486 P5
LOOP 11+m 6 5
Again: : dec cx ; 2 1 1 jne Again ; 7+m 3 1 ; --- --- -- ; 9+m 4 2
Der Geschwindigkeitszuwachs (386: 22%, 486: 50%, P5: 150%) ist doch wohl nicht zu verachten, oder? ;)
Beim P5 lässt sich das Spielchen aufgrund der Pairability (DEC: UV, JNE: PV) sogar auf einen Geschwindigkeitszuwachs von bis zu 400% hochtreiben ;), jedenfalls solange einem keine Branch Mis- prediction (3 Cycle-Penalty) dazwischenfunkt... &)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap) PM #GP[0]
PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LSL - Load Segment Limit |- - - - - * - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
------------------------------------------------------------------------- LSL r16, r|m16 NP 8 10 20|21 14|16 - 0F 03 /r
Granularity: Byte -------------------------------------------------------------------------
LSL r16, r|m16 NP 8 10 25|26 - - 0F 03 /r
Granularity: 4 KByte -------------------------------------------------------------------------
LSL r32, r|m32 NP 8 10 20|21 14|16 - 0F 03 /r
Granularity: Byte -------------------------------------------------------------------------
LSL r32, r|m32 NP 8 10 25|26 - - 0F 03 /r
Granularity: 4 KByte -------------------------------------------------------------------------
Pseudocode:
-
Beschreibung:
Mit LSL lässt sich das Segment-Limit des Descriptors ermitteln, dessen Selector man in SRC angibt.
Voraussetzungen für einen erfolgreichen Zugriff sind allerdings:
- Der Selector muß "sichtbar" sein (CPL/RPL hoch genug privilegiert) - Der Selector muß innerhalb des GDT auf einen für LSL auch zulässigen Descriptor-Typ zeigen. Zulässig sind folgende Typen:
º alle Deskriptoren vom Typ CODE und DATA º folgende SPECIAL Deskriptoren:
0001 1H 286 TSS (available) 0010 2H LDT 0011 3H 286 TSS (busy) 1001 9H 386 TSS (available) 1011 bH 386 TSS (busy)
Werden die aufgelisteten Bedingungen nicht erfüllt, setzt die CPU zum Zeichen des Mißerfolges ZF=0 und DEST bleibt unverändert.
Im Erfolgsfall dagegen wird ZF=1 gesetzt und das Segment-Limit aus dem entsprechenden Descriptor ermittelt. Ist das Granularity-Bit (Bit 23 im 2. DWord des Descriptors) gesetzt, konvertiert die CPU das Limit zuerst noch vom Page-Format (4 KByte) ins Byte-Format, ansonsten wird das Limit direkt (unbearbeitet) in DEST gespeichert.
Daß man bei DEST = r16 immer nur die unteren 16bit eines Limits erhält, dürfte einleuchten... ;)
LSL wird von der CPU nur im PM erkannt und ausgeführt. Im RM und VM wird Interrupt 6 (Invalid) ausgelöst.
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LSS - Load pointer to SS |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LSS r16, m16:16 NP 4 6 7 - - 0F B2 /r PM= PM= PM= 17 12 22 LSS r32, m16:32 NP 4 6 7 - - 0F B2 /r PM= PM= PM= 17 12 22
nur P5+: TZ = TZ + 8 bei unaccessed Descriptor
Pseudocode:
siehe LDS
Beschreibung:
LSS lädt einen an Adresse SRC liegenden 16:16 bzw. 16:32 Pointer. Der 16 bzw. 32bit-Offset des Pointers wird nach DEST (r16 bzw. r32), das 16bit Segment bzw. der 16bit Selector, nach SS geladen.
LSS wird überwiegend zur Wiederherstellung zuvor gesicherter Stacks ver- wendet.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[SS_Sel], #GP[0|SS_Sel], #PF[Code], #AC[0]
PRI PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | LTR - Load Task Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
LTR r|m16 NP 10 20 23|27 17|19 4 0F 00 /3
Pseudocode:
TR = GDT.Descriptor[Selector(r|m16)]
Beschreibung:
Mit LTR wird die Lage (genauer: Base, Limit und Attribute) des TSS (Task State Segment) für einen Task an das TR (Task Register) übergeben.
Beim TR handelt es sich um die gleiche Form eines Descriptor-Caches, wie es auch für das LDTR (Local Descriptor Table Register) zutrifft. Genau wie LLDT holt sich auch LTR Base, Limit und Attribute indirekt über den angegebenen Selector, der wiederum auf einen Descriptor im GDT zeigen muß. Bei LTR muß es sich bei diesem Descriptor natürlich um einen vom Typ TSS handeln:
TSS-Descriptor:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1|1|1|1|1|1 1 1 1| | | | | | |f e d c b a 9 8|7|6|5|4|3 2 1 0|f|e d|c|b a 9 8|7 6 5 4 3 2 1 0| | | | | | | | | | | | | | | | | |A| | | D | | | | 4 | | | | |V| Limit | | P | | | | | Base 24..31 |G|0|0|L| 16-19 |P| L |0| DTYPE | Base 16..23 | +---------------+-+-+-+-+-------+-+---+-+-------+---------------+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1| | |f e d c b a 9 8 7 6 5 4 3 2 1 0|f e d c b a 9 8 7 6 5 4 3 2 1 0| | | | 0 | Base 0..15 | Limit 0..15 | +-------------------------------+-------------------------------+
DTYPE = Descriptor-Typ:
0001 1H 286 TSS (available) 0011 3H 286 TSS (busy) 1001 9H 386 TSS (available) 1011 bH 386 TSS (busy)
DPL = Descriptor Privileg Level (0..3) P = Segment present (1: ja, 0: nein) AVL = Available (Bit zur freien Verwendung für den Programmierer) G = Granularity (1: 4KByte, 0: Byte)
Handelt es sich um einen gültigen TSS-Descriptor, wird das TR mit den besagten Descriptor-Werten für das TSS geladen.
HINWEIS: LTR setzt zwar automatisch das Busy-Bit (ist in DTYPE enthal- ten, Bit 9 im 2. DWord des Descriptors), führt aber KEINEN Task Switch durch.
LTR ist eine privilegierte Instruction und kann nur bei CPL0 ausgeführt werden.
LTR wird von der CPU nur im PM erkannt und ausgeführt. Im RM und VM wird Interrupt 6 (Invalid) ausgelöst.
LTR wird üblicherweise zur Initialisierung des ersten Tasks in einer PM- Umgebung eingesetzt. Das jeweilige Laden des TR geschieht danach üblich- erweise durch die Task Switches selbst.
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #NP[Selector], #SS[0], #GP[0|Selector], #PF[Code]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | MOV - Move Data |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
MOV r|m8, r8 UV 1 1 2 2|3 2|9+EA 88 /r MOV r|m16, r16 UV 1 1 2 2|3 2|9+EA 89 /r MOV r|m32, r32 UV 1 1 2 - - 89 /r MOV r8, r|m8 UV 1 1 2|4 2|5 2|8+EA 8A /r MOV r16, r|m16 UV 1 1 2|4 2|5 2|8+EA 8B /r MOV r32, r|m32 UV 1 1 2|4 - - 8B /r MOV AL, moffs8 UV 1 1 4 5 10 A0 MOV AX, moffs16 UV 1 1 4 5 10 A1 MOV EAX, moffs32 UV 1 1 4 - - A1 MOV moffs8, AL UV 1 1 4 3 10 A2 MOV moffs16, AX UV 1 1 4 3 10 A3 MOV moffs32, EAX UV 1 1 4 - - A3 MOV r8, imm8 UV 1 1 2 2 4 B0+rb MOV r16, imm16 UV 1 1 2 2 4 B8+rw MOV r32, imm32 UV 1 1 2 - - B8+rd MOV r|m8, imm8 UV 1 1 2 2|3 4|10+EA C6 /0 MOV r|m16, imm16 UV 1 1 2 2|3 4|10+EA C7 /0 MOV r|m32, imm32 UV 1 1 2 - - C7 /0 MOV r|m16, sreg UV 1 3 2 2|3 2|9+EA 8C /r MOV sreg, r|m16 UV 2|3 3|9 2|5 2|5 2|8+EA 8E /r PM= PM= PM= - 11|12º 18|19 17|19
º nur P5 im PM: MOV SS, r|m16 = 17|17
Pseudocode:
if DEST <> sreg then
DEST = SRC
else
; MOV sreg, r|m16
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
if sreg = SS then
; Stack Segment
if SS_Sel = 0 then #GP[0] endif
if SS_Sel_out_of_DesTblLimits then #GP[SS_Sel] endif
if CPL <> SS_Sel.RPL then #GP[SS_Sel] endif
if SS_Des.AccessRights = not_writable then #GP[SS_Sel] endif
if CPL <> SS_Des.DPL then #GP[SS_Sel] endif
if SS_not_present then #SS[SS_Sel] endif
InternalLoad SS with Selector InternalLoad SS with Descriptor
else
; DS, ES, FS (386+) oder GS (386+)
if sreg.Sel < 4 then
; Null Selector
Clear Descriptor.ValidBit
else
; normaler Selector
if sreg_SelIdx_out_of_DesTblLimits then #GP[sreg_Sel] endif
if sreg_Des.AccessRights <> (Data OR ReadableCode) then #GP[sreg_Sel] endif
if sreg_Des.AccessRights = (Data OR Non_conforming_CS) then if ((CPL > sreg_Des.DPL) OR (RPL > sreg_Des.DPL)) then #GP[sreg_Sel] endif endif
if sreg_not_present then #NP[sreg_Sel] endif
InternalLoad sreg with Descriptor
endif
InternalLoad sreg with Selector (incl. RPL Bits)
endif
else
; MOV sreg, r|m16 im RM oder VM
DEST = SRC
endif
endif
Beschreibung:
MOV lädt den Wert aus SRC und speichert ihn in DEST.
Mit MOV sreg, r|m16 können die Segmentregister DS, ES, FS und GS im PM mit einem NullSelector (0..3) geladen werden, ohne daß es zu einer all- gemeinen Schutzverletzung (#GP) kommt. Dazu kommt es erst, wenn später ein Zugriff auf ein solches Segment erfolgt... &)
Zur Gewährleistung der Integritätssicherheit des Stacks sperren CPUs ab dem 286 bei einem MOV SS, r|m16 automatisch alle IRQs für die Dauer die- ser und der unmittelbar folgenden Instruction (i.d.R. ein MOV eSP, x).
Diese IRQ-Sperrung existiert eigentlich schon seit den 8086 CPUs, arbei- tet dort aber teilweise fehlerhaft und lässt trotzdem IRQs zu. Um auch für 8086 CPUs die Integrität sicherzustellen, sollten Stackmanipulatio- nen dort sicherheitshalber mit einem CLI/STI Paar umrahmt werden.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #NP[sreg_Sel], #SS[SS_Sel], #GP[0|SS_Sel|sreg_Sel], #PF[Code], #AC[0]
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | MOV - Move to/from Special Registers (CRn, DRn, TRn) |? - - - ? ? ? ? ?| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode ------------------------------------------------------------------------- MOV CR0, r32 NP 22 4 10 - - 0F 22 /r MOV CR2, r32 NP 12 4 4 - - 0F 22 /r MOV CR3, r32 NP 21 4 5 - - 0F 22 /r MOV CR4, r32 NP 14 - - - - 0F 22 /r MOV r32, CR0 NP 4 4 6 - - 0F 20 /r MOV r32, CR2 NP 4 4 6 - - 0F 20 /r MOV r32, CR3 NP 4 4 6 - - 0F 20 /r MOV r32, CR4 NP 4 - - - - 0F 20 /r ------------------------------------------------------------------------- MOV DR0, r32 NP 11 11 22 - - 0F 23 /r MOV DR1, r32 NP 11 11 22 - - 0F 23 /r MOV DR2, r32 NP 11 11 22 - - 0F 23 /r MOV DR3, r32 NP 11 11 22 - - 0F 23 /r MOV DR4, r32º NP 12 11 22 - - 0F 23 /r MOV DR5, r32º NP 12 11 22 - - 0F 23 /r MOV DR6, r32 NP 11 11 16 - - 0F 23 /r MOV DR7, r32 NP 11 11 16 - - 0F 23 /r MOV r32, DR0 NP 11 10 22 - - 0F 21 /r MOV r32, DR1 NP 11 10 22 - - 0F 21 /r MOV r32, DR2 NP 11 10 22 - - 0F 21 /r MOV r32, DR3 NP 11 10 22 - - 0F 21 /r MOV r32, DR4º NP 11 10 22 - - 0F 21 /r MOV r32, DR5º NP 11 10 22 - - 0F 21 /r MOV r32, DR6 NP 11 10 14 - - 0F 21 /r MOV r32, DR7 NP 11 10 14 - - 0F 21 /r ------------------------------------------------------------------------- MOV TR3, r32§ NP - 6 - - - 0F 26 /r MOV TR6, r32§ NP - 4 12 - - 0F 26 /r MOV TR7, r32§ NP - 4 12 - - 0F 26 /r MOV r32, TR3§ NP - 3 - - - 0F 24 /r MOV r32, TR6§ NP - 4 12 - - 0F 24 /r MOV r32, TR7§ NP - 4 12 - - 0F 24 /r -------------------------------------------------------------------------
º Ist CR4.DE = 0 sind Zugriffe auf DR4 und DR5 (Mirror DR6/DR7) erlaubt Ist CR4.DE = 1 resultieren Zugriffe auf DR4 und DR5 in Int 6 (Invalid)
§ direkter Zugriff auf Test Register (TR3, TR6 und TR7) ist nur auf 386 und 486 CPUs möglich. Mit dem P5 wurden die Test Register in die MSRs (Model Specific Registers) ausgelagert.
Pseudocode:
DEST = SRC
Beschreibung:
Mit diesen Instructions können verschiedene Spezialregister beeinflusst werden. Nähere Informationen zu den Registern befinden sich in BAW0007.
Alle MOVs in Verbindung mit Spezialregistern sind privilegierte Instruc- tions und können nur bei CPL0 ausgeführt werden.
Exceptions:
RM Int 6 (Invalid), Int 13 (Wrap|CR4.ReservedBitSet) VM Int 6 (Invalid), #GP[0] PM #UD, #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | MOVS? - Move Data from String to String |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
MOVSB NP 4 7 7 5 18 A4 MOVSW NP 4 7 7 5 18 A5 MOVSD NP 4 7 7 - - A5 MOVS m8, m8 NP 4 7 7 5 18 A4 MOVS m16, m16 NP 4 7 7 5 18 A5 MOVS m32, m32 NP 4 7 7 - - A5
Pseudocode:
if AddrSize = 16 then SRCIdx = SI DESTIdx = DI else SRCIdx = ESI DESTIdx = EDI endif
case OperandSize of
Byte
if DF = 0 then IncDec = 1 else IncDec = -1 endif [byte ptr ES:DESTIdx] = [byte ptr SRCIdx]
Word
if DF = 0 then IncDec = 2 else IncDec = -2 endif [word ptr ES:DESTIdx] = [word ptr SRCIdx]
DWord
if DF = 0 then IncDec = 4 else IncDec = -4 endif [dword ptr ES:DESTIdx] = [dword ptr SRCIdx]
endcase
SRCIdx = SRCIdx + IncDec DESTIdx = DESTIdx + IncDec
Beschreibung:
MOVS? kopiert das Byte, Word oder DWord auf das SRCIdx zeigt, an die Po- sition, auf die ES:DESTIdx zeigt. Wird MOVS? ohne Segment Prefix einge- setzt, ist DS automatisch Standardsegment für SRCIdx (DS:eSI).
Abschließend werden in Abhängigkeit vom DF-Zustand SRCIdx und DESTIdx um 1, 2 od. 4 (Byte, Word oder DWord) in- (DF=0) bzw. dekrementiert (DF=1).
DF ist z.B. über die Instructions CLD (DF=0) bzw. STD (DF=1) steuerbar.
Im Zusammenhang mit REP? Prefixes und dem eCX-Register können auch ganze Byte-, Word- oder DWord-Strings kopiert werden.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | MOVSX - Move Data with Sign-Extension |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
MOVSX r16, r|m8 NP 3 3 3|6 - - 0F BE /r MOVSX r32, r|m8 NP 3 3 3|6 - - 0F BE /r MOVSX r32, r|m16 NP 3 3 3|6 - - 0F BE /r
Pseudocode:
DEST = SignExtend(SRC)
Beschreibung:
MOVSX erweitert das Byte bzw. Word aus SRC vorzeichenbehaftet in ein Word bzw. DWord und legt es in DEST ab.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | MOVZX - Move Data with Zero-Extension |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
MOVZX r16, r|m8 NP 3 3 3|6 - - 0F B6 /r MOVZX r32, r|m8 NP 3 3 3|6 - - 0F B6 /r MOVZX r32, r|m16 NP 3 3 3|6 - - 0F B7 /r
Pseudocode:
DEST = ZeroExtend(SRC)
Beschreibung:
MOVZX füllt das Byte bzw. Word aus SRC mit Nullbits bis zur Größe von Word bzw. DWord auf und legt das Ergebnis in DEST ab.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | MUL - Multiply (unsigned) |* - - - ? ? ? ? *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
MUL AL, r|m8 NP 11 13-18 9-14| 13|16 70-77| F6 /4 12-17 76-83+EA MUL AX, r|m16 NP 11 13-26 9-22| 21|24 113-118| F7 /4 12-25 124-139+EA MUL EAX, r|m32 NP 10 13-42 9-38| - - F7 /4 12-41
Pseudocode:
case OperandSize of
Byte: Multiplicand = AL Multiplicator = r|m8 ProductReg = AX
Word: Multiplicand = AX Multiplicator = r|m16 ProductReg = DX:AX
DWord: Multiplicand = EAX Multiplicator = r|m32 ProductReg = EDX:EAX
endcase
ProductReg = Multiplicand * Multiplicator
if Hi(ProductReg) = 0 then OF = 0 CF = 0 else OF = 1 CF = 1 endif
Beschreibung:
MUL führt eine Multiplikation ohne Beachtung des Vorzeichens (Unsigneds) durch. Welche Angabe dabei als Resultat, Multiplikant und Multiplikator interpretiert wird, kann dem Pseudocode entnommen werden.
Beispiel:
mov al, 7 ; Multiplikant mov bl, 8 ; Multiplikator mul bl =============== ax = 38H = 56 ; Produkt
OF und CF sind gelöscht, wenn der höchstwertige Teil des Produktes (AH, DX bzw. EDX) gleich Null ist.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | NEG - Negation (Two's Complement) |* - - - * * - * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
NEG r|m8 NP 1|3 1|3 2|6 2|7 3|16+EA F6 /3 NEG r|m16 NP 1|3 1|3 2|6 2|7 3|16+EA F7 /3 NEG r|m32 NP 1|3 1|3 2|6 - - F7 /3
Pseudocode:
if Operand = 0 then CF = 0 else CF = 1 endif
Operand = 0 - Operand
Beschreibung:
NEG konvertiert einen Operanden in sein 2er-Komplement. Wie der Name be- reits erahnen lässt, wird ein Wert durch NEG (signed) negiert. Beispiel:
mov al, 00000011b ; 3 (03H) neg al ==================== al= 11111101b ; -3 (fdH) signed
Das ganze funktioniert natürlich auch anders herum:
mov al, 11111101b ; -3 (fdH) signed neg al ==================== al= 00000011b ; 3 (03H)
Bei der Assemblerprogrammierung wird NEG u.a. häufig zur Verwirklichung von ABS-Funktionen (Absolutwert) eingesetzt.
HINWEIS: NEG wird manchmal mit NOT verwechselt, oder gar gleich gesetzt. Dies ist natürlich nicht der Fall: NEG konvertiert einen Wert in das 2er-, NOT einen Wert ins 1er-Komplement.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | NOP - No Operation |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
NOP UV 1 1 3 3 3 90
Pseudocode:
-
Beschreibung:
NOPs dienen hauptsächlich der Ausrichtung (Alignment) von Sprungzielen, oder der Verwirklichung kurzer Verzögerungen (z.B. nach Portzugriffen).
HINWEIS: In manchen Dokumentationen wird NOP als Alias für XCHG AL, AL angegeben. Das ist definitiv falsch (XCHG AL, AL = 86 C0).
HINWEIS: NOP und XCHG AX, AX (bzw. XCHG EAX, EAX im PM) besitzen den gleichen Opcode (90H) und werden von den Assemblerpaketen ent- sprechend gleich behandelt. Von allen mir bekannten Debuggern wird Opcode 90H jedoch IMMER als NOP reassembliert, was Leute, die nach ihren XCHG AX, AX Anweisungen suchen, natürlich leicht verzweifeln lassen dürfte... ;)
HINWEIS: Ab 486er CPUs weichen die offiziellen Taktzyklenangaben für NOP und XCHG AX, AX voneinander ab (486 = 1:3, P5 = 1:2). Daß das kompletter Unsinn ist, dürfte einleuchten...
Vorsicht bei Versuchen, exakte Verzögerungen durch mehrere auf- einanderfolgende NOPs auf P5+ CPUs zu realisieren. NOPs sind ab diesen CPUs "unglücklicherweise" auch noch UV-pairable... &)
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | NOT - Negation (One's Complement) |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
NOT r|m8 NP 1|3 1|3 2|6 2|7 3|16+EA F6 /2 NOT r|m16 NP 1|3 1|3 2|6 2|7 3|16+EA F7 /2 NOT r|m32 NP 1|3 1|3 2|6 - - F7 /2
Pseudocode:
Operand = NOT Operand
Beschreibung:
NOT invertiert (flipped, toggled) jedes Bit des Operanden, d.h. aus je- der 1 wird eine 0 und umgekehrt. Beispiel:
mov al, 11110000b ; f0H not al ==================== al= 00001111b ; 0fH
HINWEIS: NOT wird manchmal mit NEG verwechselt, oder gar gleich gesetzt. Dies ist natürlich nicht der Fall: NOT konvertiert einen Wert in das 1er-, NEG einen Wert ins 2er-Komplement.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | OR - Logical Inclusive OR |0 - - - * * ? * 0| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
OR AL, imm8 UV 1 1 2 3 4 0C ib OR AX, imm16 UV 1 1 2 3 4 0D iw OR EAX, imm32 UV 1 1 2 - - 0D id OR r|m8, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 80 /1 ib OR r|m16, imm16 UV 1|3 1|3 2|7 3|7 4|17+EA 81 /1 iw OR r|m32, imm32 UV 1|3 1|3 2|7 - - 81 /1 id OR r|m16, imm8 UV 1|3 1|3 2|7 - - 83 /1 ib OR r|m32, imm8 UV 1|3 1|3 2|7 - - 83 /1 ib OR r|m8, r8 UV 1|3 1|3 2|7 2|7 3|16+EA 08 /r OR r|m16, r16 UV 1|3 1|3 2|7 2|7 3|16+EA 09 /r OR r|m32, r32 UV 1|3 1|3 2|7 - - 09 /r OR r8, r|m8 UV 1|2 1|2 2|6 2|7 3|9+EA 0A /r OR r16, r|m16 UV 1|2 1|2 2|6 2|7 3|9+EA 0B /r OR r32, r|m32 UV 1|2 1|2 2|6 - - 0B /r
Pseudocode:
DEST = DEST OR SRC CF = 0 OF = 0
Beschreibung:
Mit den korrespondierenden Bits von DEST und SRC wird eine logische ODER Verknüpfung durchgeführt. Ergebnis der Verknüpfung wird in DEST abge- legt. Wahrheitstabelle OR:
0 OR 0 = 0 0 OR 1 = 1 1 OR 0 = 1 1 OR 1 = 1
OR wird üblicherweise eingesetzt, um bestimmte Bits in einem Byte, Word oder DWord zu setzen, falls sie es noch nicht sind. Beispiel:
mov al, 10110101B or al, 00001111B ==================== al = 10111111B
Ist DEST ein Word oder DWord und SRC ein imm8, wird SRC von der CPU vor der Addition automatisch vorzeichenbehaftet auf Word/DWord erweitert.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | OUT - Output to Port |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
OUT imm8, AL NP 12 16 10 3 10 E6 ib PM= PM= PM= 9º,26§ 11º,31§ 4º,24§ VM=24 VM=29 VM=24 OUT imm8, AX NP 12 16 10 3 10 E7 ib PM= PM= PM= 9º,26§ 11º,31§ 4º,24§ VM=24 VM=29 VM=24 OUT imm8, EAX NP 12 16 10 - - E7 ib PM= PM= PM= 9º,26§ 11º,31§ 4º,24§ VM=24 VM=29 VM=24 OUT DX, AL NP 12 16 11 3 8 EE PM= PM= PM= 9º,25§ 11º,31§ 5º,25§ VM=24 VM=29 VM=25 OUT DX, AX NP 12 16 11 3 8 EF PM= PM= PM= 9º,25§ 11º,31§ 5º,25§ VM=24 VM=29 VM=25 OUT DX, EAX NP 12 16 11 - - EF PM= PM= PM= 9º,25§ 11º,31§ 5º,25§ VM=24 VM=29 VM=25
º alle: wenn CPL ó IOPL
§ 386 : wenn CPL > IOPL 486+: wenn CPL ò IOPL
Pseudocode:
if (PE = 1) AND ((VM = 1) OR (CPL > IOPL)) then if NOT IO_Permission(DEST) then #GP[0] endif endif
[DEST] = SRC
Beschreibung:
OUT beschreibt den Port an Adresse DEST mit dem Wert aus SRC. Portadres- sen im Bereich 0..255 können direkt (imm8), höherliegende Ports nur via DX adressiert werden.
HINWEIS: Bei Verwendung der DX-Adressierung ist die tatsächlich nutzbare Datenbreite nicht automatisch auch gleich 16bit. Werden z.B. Adapterkarten adressiert, die sich in einem ISA-Slot befinden, reduziert sich die tatsächlich ausgewertete Datenbreite auf die Bits 0..9, d.h. den Adressbereich 0..1023 (000..3ffH).
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #GP[0], #PF[Code] PM #SS[0], #GP[0], #PF[Code]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | OUTS? - Output String to Port |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
OUTSB NP 13 17 14 5 - 6E PM= PM= PM= 10º,27§ 10º,32§ 8º,28§ VM=25 VM=30 VM=28 OUTSW NP 13 17 14 5 - 6F PM= PM= PM= 10º,27§ 10º,32§ 8º,28§ VM=25 VM=30 VM=28 OUTSD NP 13 17 14 - - 6F PM= PM= PM= 10º,27§ 10º,32§ 8º,28§ VM=25 VM=30 VM=28 OUTS DX, r|m8 NP 13 17 14 5 - 6E PM= PM= PM= 10º,27§ 10º,32§ 8º,28§ VM=25 VM=30 VM=28 OUTS DX, r|m16 NP 13 17 14 5 - 6F PM= PM= PM= 10º,27§ 10º,32§ 8º,28§ VM=25 VM=30 VM=28 OUTS DX, r|m32 NP 13 17 14 - - 6F PM= PM= PM= 10º,27§ 10º,32§ 8º,28§ VM=25 VM=30 VM=28
º alle: wenn CPL ó IOPL
§ 386 : wenn CPL > IOPL 486+: wenn CPL ò IOPL
Pseudocode:
if AddrSize = 16 then SRCIdx = SI else SRCIdx = ESI endif
if (PE = 1) AND ((VM = 1) OR (CPL > IOPL)) then if NOT IO_Permission(DEST) then #GP[0] endif endif
case OperandSize of
Byte
if DF = 0 then IncDec = 1 else IncDec = -1 endif OUTSB[Port(DX)] = byte ptr SRCIdx
Word
if DF = 0 then IncDec = 2 else IncDec = -2 endif OUTSW[Port(DX)] = word ptr SRCIdx
DWord
if DF = 0 then IncDec = 4 else IncDec = -4 endif OUTSD[Port(DX)] = dword ptr SRCIdx
endcase
SRCIdx = SRCIdx + IncDec
Beschreibung:
OUTS? schreibt ein Byte, Word oder DWord von der Adresse SRCIdx in den Port DX. Wird OUTS? kein Segment Prefix vorangestellt, wird automatisch DS als Standardsegment benutzt (DS:eSI).
Abschließend wird in Abhängigkeit vom DF-Zustand SRCIdx um 1, 2 oder 4 (Byte, Word oder DWord) in- (DF=0) bzw. dekrementiert (DF=1).
DF ist z.B. über die Instructions CLD (DF=0) bzw. STD (DF=1) steuerbar.
Im Zusammenhang mit REP? Prefixes und dem eCX-Register können auch mehr- ere Bytes, Words oder DWords aus dem Speicher nacheinander in den spezi- fizierten Port geschrieben werden.
HINWEIS: DX bedeutet normalerweise eine Datenbreite von 16bit. Ob diese 16bit tatsächlich in voller Breite benutzt werden können, hängt aber vom verwendeten Bussystem ab. Der ISA-Bus wertet z.B. nur 10bit-Portadressen (000..3ffH) aus.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #GP[0], #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | POP - Pop Top of Stack |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
POP r16 UV 1 4 4 5 8 58+rw POP r32 UV 1 4 4 - - 58+rd POP m16 NP 3 6 5 5 17+EA 8F /0 POP m32 NP 3 6 5 - - 8F /0 POP DS NP 3 3 7 5 8 1F PM= PM= 21 21 POP ES NP 3 3 7 5 8 07 PM= PM= 21 20 POP FS NP 3 3 7 - - 0F A1 PM= - 21 POP GS NP 3 3 7 - - 0F A9 PM= - 21 POP SS NP 3 3 7 5 8 17 PM= PM= 21 20
Pseudocode:
if StackAddrSize = 16 then
if OperandSize = 16 then DEST = word ptr SS:SP Inc(SP, 2) else DEST = dword ptr SS:SP Inc(SP, 4) endif
else
; 32bit Stack
if OperandSize = 16 then DEST = word ptr SS:ESP Inc(ESP, 2) else DEST = dword ptr SS:ESP Inc(ESP, 4) endif
endif
if Operand = sreg then
; POP Segmentregister
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
if sreg = SS then
; Stack Segment
if SS_Sel = 0 then #GP[0] endif
if SS_Sel_out_of_DesTblLimits then #GP[SS_Sel] endif
if CPL <> SS_Sel.RPL then #GP[SS_Sel] endif
if SS_Des.AccessRights = not_writable then #GP[SS_Sel] endif
if CPL <> SS_Des.DPL then #GP[SS_Sel] endif
if SS_not_present then #SS[SS_Sel] endif
InternalLoad SS with Selector InternalLoad SS with Descriptor
else
; DS, ES, FS (386+) oder GS (386+)
if sreg.Sel < 4 then
; Null Selector
Clear Descriptor.ValidBit
else
; normaler Selector
if sreg_SelIdx_out_of_DesTblLimits then #GP[sreg_Sel] endif
if sreg_Des.AccessRights <> (Data OR ReadableCode) then #GP[sreg_Sel] endif
if sreg_Des.AccessRights = (Data OR Non_conforming_CS) then if ((CPL > sreg_Des.DPL) OR (RPL > sreg_Des.DPL)) then #GP[sreg_Sel] endif endif
if sreg_not_present then #NP[sreg_Sel] endif
InternalLoad sreg with Descriptor
endif
InternalLoad sreg with Selector (incl. RPL Bits)
endif
endif
endif
Beschreibung:
POP ist das Gegenstück zu PUSH und holt das oberste Word bzw. DWord vom Stack und legt es in DEST ab.
POP IP und POP CS:eIP existieren namentlich nicht, dafür sind die Mne- monics RET bzw. RETF einzusetzen. Ein reines POP CS existiert logischer- weise überhaupt nicht, wäre wohl auch meist recht sinnlos...;)
Mit POP sreg können die Segmentregister DS, ES, FS und GS im PM mit ei- nem NullSelector (0..3) geladen werden, ohne daß es zu einer allgemeinen Schutzverletzung (#GP) kommt. Dazu kommt es erst, wenn ein Zugriff auf ein solches Segment erfolgt... &)
Zur Gewährleistung der Integritätssicherheit des Stacks sperren CPUs ab dem 286 bei einem POP SS automatisch alle IRQs für die Dauer dieser und der unmittelbar folgenden Instruction (i.d.R. ein MOV eSP, x).
Diese IRQ-Sperrung existiert eigentlich schon seit den 8086 CPUs, arbei- tet dort aber teilweise fehlerhaft und lässt trotzdem IRQs zu. Um auch für 8086 CPUs die Integrität sicherzustellen, sollten Stackmanipulatio- nen sicherheitshalber mit einem CLI/STI Paar umrahmt werden.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #NP[sreg_Sel], #SS[SS_Sel], #GP[0|SS_Sel|sreg_Sel], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | POPA - Pop all General Registers from Stack |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
POPA NP 5 9 24 19 - 61 POPAD NP 5 9 24 - - 61
Pseudocode:
if OperandSize = 16 POP DI POP SI POP BP ADD eSP, 2 POP BX POP DX POP CX POP AX else POP EDI POP ESI POP EBP ADD eSP, 4 POP EBX POP EDX POP ECX POP EAX endif
Beschreibung:
POPA ist das Gegenstück zu PUSHA und holt die acht obersten Words bzw. DWords wieder vom Stack. Wie im Pseudocode zu sehen, werden dadurch nur sieben der acht existierenden Arbeitsregister (eDI, eSI, eBP, eBX, eDX, eCX und eAX, in genau dieser Reihenfolge) direkt mit Werten vom Stack geladen.
Das achte Arbeitsregister (eSP) erhält seinen endgültigen Wert natürlich durch die Erhöhung des eSP beim zuletzt ausgeführten POP (POP eAX), und nicht wie manchmal angenommen wird, direkt aus dem viertem auf dem Stack liegenden Word bzw. DWord... &)
HINWEIS: Wie ein POPA oder POPAD schlußendlich übersetzt wird, ist vom angewandten Assemblerpaket abhängig. Einige Pakete werten POPA automatisch als 16bit-, und POPAD als 32bit-Anweisung. Andere Pakete machen keinen Unterschied zwischen POPA und POPAD, son- dern orientieren sich an der Voreinstellung des gewählten Spei- cher-, bzw. Stackmodells.
Exceptions:
RM Int 12, Int 13 (Wrap) VM #SS[0], #GP[0], #PF[Code] PM #SS[0], #PF[Code]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | POPF - Pop Top of Stack into eFLAGS Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
POPF NP 6 9 5 5 8 9D PM= PM= 4 6 POPFD NP 6 9 5 - - 9D PM= PM= 4 6
Pseudocode:
if (VM = 0) then
; reiner Protected Mode oder Real Mode
if OperandSize = 32 then TmpD = POPD(TopOfStack AND 00277fd7H) TmpW = Lo(TmpD) else TmpW = POPW(TopOfStack AND 7fd7H) endif
if CPL <> 0 then TmpW = TmpW AND cfffH endif
if CPL > TmpW.IOPL TmpW = TmpW AND fdffH endif
if OperandSize = 32 then TmpD = TmpD AND ffff0000H OR TmpW EFLAGS = EFLAGS OR TmpD else FLAGS = FLAGS OR TmpW endif
else
; Virtual Mode
if IOPL <> 3 then #GP[0] else if OperandSize = 16 then FLAGS = POPW(TopOfStack AND cfd7H) else TmpD = POPD(TopOfStack AND ffe4cfffH) EFLAGS = (EFLAGS AND 001b3000H) OR TmpD endif endif
endif
Beschreibung:
POPF bzw. POPFD sind die Gegenstücke zu PUSHF bzw. PUSHFD. Sie holen das oberste Word bzw. DWord vom Stack und speichern es nach Durchlaufen ver- schiedener Berabeitungsstufen in FLAGS bzw. EFLAGS.
Im PM wird eine Änderung des IOPL durch POPF/POPFD nur bei CPL0 vollzo- gen, ansonsten wird einfach mit dem alten IOPL weitergearbeitet, d.h. es wird KEINE Exception ausgelöst.
Würde durch POPF/POPFD im PM das IF verändert, erfolgt die Änderung nur dann, wenn das Programm ausreichend hoch privilegiert dazu ist, anderen- falls wird auch hier KEINE Exception ausgelöst, sondern einfach unverän- dert weitergearbeitet.
Für RM-Programme treffen die beiden vorgenannten Einschränkungen natür- lich nicht zu, da RM-Programme immer CPL0 besitzen... &)
Im VM ist POPF/POPFD prinzipiell nur bei IOPL = 3 zulässig, ansonsten handelt man sich direkt eine allgemeinen Schutzverletzung (#GP[0]) ein. Eine Änderung des IOPL im VM mittels POPF/POPFD wird durch automatisches Ausblenden der entsprechenden Bits (12..13) grundsätzlich unterbunden.
Die Bits 19 und 20 (VIF und VIP) werden bei einem POPFD generell NICHT beachtet (egal, ob im RM, VM oder PM), sie werden in jedem Fall ausge- blendet. Gleiches gilt übrigens auch für die Bits 16 und 17 (RF und VM), jedenfalls soweit es sich um ein POPFD im VM handelt.
Der genaue Aufbau von eFLAGS ist im Topic BAW0007 (CPU-Registersatz) be- schrieben.
HINWEIS: Wie ein POPF oder POPFD schlußendlich übersetzt wird, ist vom angewandten Assemblerpaket abhängig. Einige Pakete werten POPF automatisch als 16bit-, und POPFD als 32bit-Anweisung. Andere Pakete machen keinen Unterschied zwischen POPF und POPFD, son- dern orientieren sich an der Voreinstellung des gewählten Spei- cher-, bzw. Stackmodells.
Exceptions:
RM Int 12, Int 13 (Wrap) VM #SS[0], #GP[0] PM #SS[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | PUSH - Push Operand onto Stack |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
PUSH r16 UV 1 1 2 3 11 50+rw PUSH r32 UV 1 1 2 - - 50+rd PUSH imm8 UV 1 1 2 3 - 6A PUSH imm16 UV 1 1 2 3 - 68 PUSH imm32 UV 1 1 2 - - 68 PUSH m16 NP 1 4 5 5 16+EA FF /6 PUSH m32 NP 1 4 5 - - FF /6 PUSH CS NP 1 3 2 3 10 0E PUSH DS NP 1 3 2 3 10 1E PUSH ES NP 1 3 2 3 10 06 PUSH FS NP 1 3 2 - - 0F A0 PUSH GS NP 1 3 2 - - 0F A8 PUSH SS NP 1 3 2 3 10 16
Pseudocode:
if StackAddrSize = 16 then
if OperandSize = 16 then Dec(SP, 2) word ptr SS:SP = SRC else Dec(SP, 4) dword ptr SS:SP = SRC endif
else
; 32bit Stack
if OperandSize = 16 then Dec(ESP, 2) word ptr SS:ESP = SRC else Dec(ESP, 4) dword ptr SS:ESP = SRC endif
endif
Beschreibung:
PUSH legt ein Word bzw. DWord (SRC) auf der Spitze des Stacks ab.
HINWEIS: CPUs ab dem 286 behandeln die Variante PUSH SP anders, als ihre Vorgänger. Während 286+ CPUs erst den Wert von SP auf den Stack schieben und SP danach dekrementieren, machen deren Vorgänger dies genau umgekehrt.
HINWEIS: Bei einem PUSH im RM geht die CPU in den ShutdownMode, wenn eSP zu diesem Zeitpunkt = 1 ist. Es wird KEINE Exception ausgelöst.
Exceptions:
RM Int 12, Int 13 (Wrap) VM #SS[0], #GP[0], #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | PUSHA - Push all General Registers onto Stack |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
PUSHA NP 5 11 18 17 - 60 PUSHAD NP 5 11 18 - - 60
Pseudocode:
if OperandSize = 16 TmpW = SP PUSH AX PUSH CX PUSH DX PUSH BX PUSH TmpW PUSH BP PUSH SI PUSH DI else TmpD = ESP PUSH EAX PUSH ECX PUSH EDX PUSH EBX PUSH TmpD PUSH EBP PUSH ESI PUSH EDI endif
Beschreibung:
PUSHA legt die zum Ausführungszeitpunkt aktuellen Werte der acht allge- meinen Arbeitsregister (eAX, eCX, eDX, eBX, eSP, eBP, eSI und eDI, genau in dieser Reihenfolge) auf den Stack.
Für eSP wird der Wert gesichert, den eSP VOR Ausführung von PUSHA hatte.
HINWEIS: Wie ein PUSHA oder PUSHAD schlußendlich übersetzt wird, ist vom angewandten Assemblerpaket abhängig. Einige Pakete werten PUSHA automatisch als 16bit-, und PUSHAD als 32bit-Anweisung. Andere Pakete wiederum unterscheiden PUSHA und PUSHAD nicht, sondern orientieren sich generell an der Voreinstellung des gewählten Speicher-, bzw. Stackmodells.
HINWEIS: Bei einem PUSHA/PUSHAD im RM geht die CPU in den ShutdownMode, wenn eSP zu diesem Zeitpunkt = 1, 3 oder 5 ist. In diesen Fäl- len wird KEINE Exception ausgelöst.
Exceptions:
RM Int 13 (Wrap) VM #GP[0], #PF[Code] PM #SS[0], #PF[Code]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | PUSHF - Push eFLAGS Register onto Stack |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
PUSHF NP 4 4 4 3 10 9C PM= PM= 3 3 PUSHFD NP 4 4 4 - - 9C PM= PM= 3 3
Pseudocode:
if (VM = 0) then
; reiner Protected Mode oder Real Mode
if OperandSize = 32 then PUSH (EFLAGS AND 00fcffffH) else PUSH FLAGS endif
else
; Virtual Mode
if IOPL <> 3 then #GP[0] else if OperandSize = 32 then PUSH (EFLAGS AND 00fcffffH) else PUSH FLAGS endif endif
endif
Beschreibung:
PUSHF bzw. PUSHFD legen den momentanen Inhalt von FLAGS bzw. EFLAGS auf den Stack (Details zu eFLAGS siehe BAW0007, CPU-Registersatz).
Im VM ist PUSHF/PUSHFD prinzipiell nur bei IOPL = 3 zulässig. Bei höher- privilegiertem IOPL dagegen wird sofort Exception 13 (#GP, General Pro- tection Fault) ausgelöst.
Die Bits 16 und 17 (RF und VM) werden bei PUSHF/PUSHFD von der CPU gene- rell ausgeblendet, egal, ob die CPU im RM, VM oder PM arbeitet.
HINWEIS: Wie ein PUSHF oder PUSHFD schlußendlich übersetzt wird, ist vom angewandten Assemblerpaket abhängig. Einige Pakete werten PUSHF automatisch als 16bit-, und PUSHFD als 32bit-Anweisung. Andere Pakete wiederum unterscheiden PUSHF und PUSHFD nicht, sondern orientieren sich generell an der Voreinstellung des gewählten Speicher-, bzw. Stackmodells.
HINWEIS: Bei einem PUSHF/PUSHFD im RM geht die CPU in den ShutdownMode, wenn eSP zu diesem Zeitpunkt = 1, 3 oder 5 ist. In diesen Fäl- len wird KEINE Exception ausgelöst.
Exceptions:
RM Int 13 (Wrap) VM #GP[0] PM #SS[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | RCL - Rotate thru Carry Left |! - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei Einfachrotationen OF = * bei Mehrfachrotationen OF = ?
Befehl Pipe P5 486 386 286 86 Opcode
RCL r|m8, 1 PU 1|3 3|4 9|10 2|7 2|15+EA D0 /2 RCL r|m16, 1 PU 1|3 3|4 9|10 2|7 2|15+EA D1 /2 RCL r|m32, 1 PU 1|3 3|4 9|10 - - D1 /2 RCL r|m8, imm8 PU 8-25| 8-30| 9|10 5|8 - C0 /2 ib 10-27 9-31 RCL r|m16, imm8 PU 8-25| 8-30| 9|10 5|8 - C1 /2 ib 10-27 9-31 RCL r|m32, imm8 PU 8-25| 8-30| 9|10 - - C1 /2 ib 10-27 9-31 RCL r|m8, CL NP 7-24| 8-30| 9|10 5|8 8+4n| D2 /2 9-26 9-31 20+4n+EA RCL r|m16, CL NP 7-24| 8-30| 9|10 5|8 8+4n| D3 /2 9-26 9-31 20+4n+EA RCL r|m32, CL NP 7-24| 8-30| 9|10 - - D3 /2 9-26 9-31
Pseudocode:
Count = SRC
while Count <> 0 do TmpCF = DEST[MSB] DEST = (DEST SHL 1) + TmpCF Dec(Count) endwhile
CF = TmpCF if SRC = 1 then OF = DEST[MSB] XOR CF else OF = ? endif
Beschreibung:
RCL rotiert DEST (9, 17 oder 33 Bits (inkl. CF)) SRC mal nach links. Bei jedem Durchlauf merkt sich die CPU zuerst das CF, verschiebt dann DEST um eine Stelle nach links (wodurch das MSB von DEST in CF landet) und fügt dann abschließend das gemerkte CF wieder in das LSB von DEST ein:
+------- DEST --------+ +--+ +---+---+- : -+---+---+ +--|CF|<--|MSB| | : | |LSB|<-+ | +--+ +---+---+- : -+---+---+ | +----------------------------------+
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Rotationsanzahl wird automatisch auf 0..31 begrenzt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | RCR - Rotate thru Carry Right |! - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei Einfachrotationen OF = * bei Mehrfachrotationen OF = ?
Befehl Pipe P5 486 386 286 86 Opcode
RCR r|m8, 1 PU 1|3 3|4 9|10 2|7 2|15+EA D0 /3 RCR r|m16, 1 PU 1|3 3|4 9|10 2|7 2|15+EA D1 /3 RCR r|m32, 1 PU 1|3 3|4 9|10 - - D1 /3 RCR r|m8, imm8 PU 8-25| 8-30| 9|10 5|8 - C0 /3 ib 10-27 9-31 RCR r|m16, imm8 PU 8-25| 8-30| 9|10 5|8 - C1 /3 ib 10-27 9-31 RCR r|m32, imm8 PU 8-25| 8-30| 9|10 - - C1 /3 ib 10-27 9-31 RCR r|m8, CL NP 7-24| 8-30| 9|10 5|8 8+4n| D2 /3 9-26 9-31 20+4n+EA RCR r|m16, CL NP 7-24| 8-30| 9|10 5|8 8+4n| D3 /3 9-26 9-31 20+4n+EA RCR r|m32, CL NP 7-24| 8-30| 9|10 - - D3 /3 9-26 9-31
Pseudocode:
Count = SRC
while Count <> 0 do TmpCF = DEST[LSB] DEST = (DEST SHR 1) + (TmpCF SHL (OperandSize-1)) Dec(Count) endwhile
CF = TmpCF if SRC = 1 then OF = DEST[MSB] XOR DEST[MSB-1] else OF = ? endif
Beschreibung:
RCR rotiert DEST (9, 17 oder 33 Bits (inkl. CF)) SRC mal nach rechts. Bei jedem Durchlauf merkt sich die CPU zuerst das CF, verschiebt danach DEST um eine Stelle nach rechts (wodurch das LSB von DEST in CF landet) und fügt abschließend das gemerkte Bit wieder in das MSB von DEST ein:
+------- DEST --------+ +---+---+- : -+---+---+ +--+ +->|MSB| | : | |LSB|-->|CF|--+ | +---+---+- : -+---+---+ +--+ | +----------------------------------+
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Rotationsanzahl wird automatisch auf 0..31 begrenzt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | RDMSR - Read Model Specific Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
RDMSR NP 20-24 - - - - 0F 32
Pseudocode:
EDX:EAX = MSR[ECX]
Beschreibung:
Mit RDMSR lässt sich der aktuelle Inhalt des ECXten MSR (Model Specific Register) auslesen. Der Inhalt des MSR wird in EDX:EAX abgelegt.
HINWEIS: MSRs sind grundsätzlich modellspezifisch, d.h. Funktion und In- halt der MSRs kann von Modell zu Modell und Prozessorgeneration zu Prozessorgeneration voneinander abweichen.
RDMSR gibt es erst ab P5 CPUs, ist aber auch dort implementationsspezi- fisch. Vor dem Einsatz von RDMSR sollte daher besser erst via CPUID ge- prüft werden, ob der Befehl überhaupt unterstützt wird (gesetztes Bit 5 in EDX nach CPUID(1))... ;)
RDMSR ist eine privilegierte Instruction und kann daher nur unter CPL0 ausgeführt werden. Der Aufruf von RDMSR mit einer ungültigen bzw. reser- vierten Registernummer (P5: 3, 10, 15, und größer 19) führt zur Auslö- sung eines General Protection Faults (#GP[0]).
Detaillierte Informationen zu den einzelnen MSRs befinden sich im Topic BAW0007 (CPU-Registersatz).
Exceptions:
RM Int 6 (Invalid), #GP[0] VM Int 6 (Invalid), #GP[0] PM #UD, #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | RDTSC - Read Time Stamp Counter |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
RDTSC NP 6,11º - - - - 0F 31
º erster Wert: CPL0, zweiter Wert CPL1..3
Pseudocode:
EDX:EAX = TSC
Beschreibung:
RDTSC liest den aktuellen Wert des TSCs (Time Stamp Counter) aus, und speichert ihn in EDX:EAX ab. Beim TSC handelt es sich um MSR (Model spe- cific Register) Nummer 16 (siehe BAW0007, CPU-Registersatz).
Ob der TSC und dessen Auslesebefehl RDTSC von der CPU überhaupt unter- stützt werden, läßt sich an gesetztem Bit 4 in EDX nach der Ausführung von CPUID(1) erkennen.
Falls existent, sollte aus Geschwindigkeitsgründen RDTSC einem RDMSR(16) immer vorgezogen werden. Zudem handelt es sich bei RDTSC um eine unpri- vilegierte Instruction, sie kann also bei jedem CPL eingesetzt werden.
Dies gilt allerdings nur solange, wie CR4.TSD = 0 ist. Ist CR4.TSD = 1, handelt man sich unter CPL1..3 bei Einsatz von RDTSC leider immer eine allgemeine Schutzverletzung (#GP[0]) ein... &)
HINWEIS: Ein direktes Gegenstück (WRTSC) existiert nicht. Um einen Wert in den TSC zu schreiben, muß man auf WRMSR(16) zurückgreifen.
Exceptions:
RM - VM #GP[0] PM #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | REP? - Repeat the following String Operation |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
REP INSB NP 11+3n 16+8n 13+6n 5+4n - F3 6C PM= PM= PM= 8+3nº, 10+8nº, 7+6nº, 25+3n§ 30+8n§ 27+6n§ VM= VM= 23+3n 27+6n REP INSW NP 11+3n 16+8n 13+6n 5+4n - F3 6D PM= PM= PM= 8+3nº, 10+8nº, 7+6nº, 25+3n§ 30+8n§ 27+6n§ VM= VM= 23+3n 27+6n REP INSD NP 11+3n 16+8n 13+6n - - F3 6D PM= PM= PM= 8+3nº, 10+8nº, 7+6nº, 25+3n§ 30+8n§ 27+6n§ VM= VM= 23+3n 27+6n REP LODSB NP 7+, 5+, - - - F3 AC 7+3n+ 7+4n+ REP LODSW NP 7+, 5+, - - - F3 AD 7+3n+ 7+4n+ REP LODSD NP 7+, 5+, - - - F3 AD 7+3n+ 7+4n+ REP MOVSB NP 6+, 5+, 5+4n 5+4n 9+17n F3 A4 13+, 13+, 13+n+ 12+3n+ REP MOVSW NP 6+, 5+, 5+4n 5+4n 9+17n F3 A5 13+, 13+, 13+n+ 12+3n+ REP MOVSD NP 6+, 5+, 5+4n - - F3 A5 13+, 13+, 13+n+ 12+3n+ REP OUTSB NP 13+4n 17+5n 12+5n 5+4n - F3 6E PM= PM= PM= 10+4nº, 11+5nº, 6+5nº, 27+4n§ 31+5n§ 26+5n§ VM= VM= 25+4n 26+5n REP OUTSW NP 13+4n 17+5n 12+5n 5+4n - F3 6F PM= PM= PM= 10+4nº, 11+5nº, 6+5nº, 27+4n§ 31+5n§ 26+5n§ VM= VM= 25+4n 26+5n REP OUTSD NP 13+4n 17+5n 12+5n - - F3 6F PM= PM= PM= 10+4nº, 11+5nº, 6+5nº, 27+4n§ 31+5n§ 26+5n§ VM= VM= 25+4n 26+5n REP STOSB NP 6+, 5+, 5+5n 4+3n 9+10n F3 AA 9n+ 7+4n+ REP STOSW NP 6+, 5+, 5+5n 4+3n 9+10n F3 AD 9n+ 7+4n+ REP STOSD NP 6+, 5+, 5+5n - - F3 AD 9n+ 7+4n+ REPE CMPSB NP 7+, 5+, 5+9n 5+9n 9+22n F3 A6 9+4n+ 7+7n+ REPE CMPSW NP 7+, 5+, 5+9n 5+9n 9+22n F3 A7 9+4n+ 7+7n+ REPE CMPSD NP 7+, 5+, 5+9n - - F3 A7 9+4n+ 7+7n+ REPE SCASB NP 7+, 5+, 5+8n 5+8n 9+15n F3 AE 9+4n+ 7+5n+ REPE SCASW NP 7+, 5+, 5+8n 5+8n 9+15n F3 AF 9+4n+ 7+5n+ REPE SCASD NP 7+, 5+, 5+8n - - F3 AF 9+4n+ 7+5n+ REPNE CMPSB NP 7+, 5+, 5+9n 5+9n 9+22n F2 A6 8+4n+ 7+7n+ REPNE CMPSW NP 7+, 5+, 5+9n 5+9n 9+22n F2 A7 8+4n+ 7+7n+ REPNE CMPSD NP 7+, 5+, 5+9n - - F2 A7 8+4n+ 7+7n+ REPNE SCASB NP 7+, 5+, 5+8n 5+8n 9+15n F2 AE 9+4n+ 7+5n+ REPNE SCASW NP 7+, 5+, 5+8n 5+8n 9+15n F2 AF 9+4n+ 7+5n+ REPNE SCASD NP 7+, 5+, 5+8n - - F2 AF 9+4n+ 7+5n+
º alle: wenn CPL ó IOPL
§ 386 : wenn CPL > IOPL 486+: wenn CPL ò IOPL
+ wenn eCX = 0 + wenn eCX > 0
+ wenn eCX = 1 + wenn eCX > 1
Pseudocode:
while eCX <> 0 do
if (Pending_IRQs) then Service_Pending_IRQs endif
Execute_String_Instruction
Dec(eCX)
if (CMPS? OR SCAS?) then if ((REPE AND ZF=0) OR (REPNE AND ZF=1)) then exit endif endif
endwhile
Beschreibung:
Das Prefix REP? (REP, REPE, REPNE) stellt man einem Stringbefehl voran, wenn man diesen mehrfach (max. eCX mal) hintereinander ausführen möchte.
REP?-Prefixe sind somit ausschließlich vor CMPS?, INS?, LODS?, MOVS?, OUTS?, SCAS? und STOS? Instructions zulässig.
REPE und REPNE (und deren Synonyme REPZ und REPNZ) werden bei vergleich- enden String-Instructions (CMPS? und SCAS?) eingesetzt, um solange zu wiederholen, bis eine Gleichheit (REPNE) bzw. eine Ungleichheit (REPE) eintritt, oder eCX = 0 ist.
REP dagegen wird nur bei reinen Blockoperationen mit INS?, LODS?, MOVS?, OUTS? und STOS? eingesetzt und grundsätzlich solange wiederholt, bis eCX = 0 ist.
HINWEIS: Durch das Voransetzen eines REP? Prefixes verbessert sich meist die Ausführungsgeschwindigkeit des jeweiligen Stringbefehles.
Der Gewinn kommt natürlich erst zum Tragen, wenn die Dauer für die einmalige Dekodierung des REP? Prefixes durch die Wiederho- lungen auch überschritten wird... ;)
Allerdings sollte man sich darüber im Klaren sein, daß die CPU vor der Ausführung der jeweils nächsten Wiederholung immer erst alle bis dahin aufgelaufenen IRQs bedient, was im ungünstigsten Fall natürlich auch zu extremem Performanceverlust führen kann.
HINWEIS: Das implizite Sperren von IRQs vor REP-Anweisungen scheint zwar vorteilhaft, sollte imo aber nur auf kurze Zeiträume beschränkt werden, um die IRQ-Timings nicht durcheinander zu bringen... ;)
Außerdem kann auf bestimmte Speicherbereiche grundsätzlich nur dann zugegriffen werden, wenn auch IRQs zugelassen sind... &)
HINWEIS: Die I/O Ports einiger älterer Adapter verkraften den durch REP? INS? bzw. REP? OUTS? eventuellen entstehenden Geschwindigkeits- zuwachs nicht. Kommt es bei der Programmierung zu Datenintegri- tätsverlusten, empfiehlt sich imo die Gegenprobe mit "normalen" INS? bzw. OUTS? Schleifen... &)
HINWEIS: Auf CPUs vor dem 386 kann es bei Kombinationen von Segment und REP? Prefixes zu Problemen kommen, wenn ein IRQ bedient werden muß, bevor CX = 0 ist. Auf diesen CPUs führt das IRET des be- dienten IRQs zu einem Rücksprung HINTER die gesamte REP?-Anwei- sung, sie wird also u.U. verlassen, bevor CX überhaupt = 0 ist. Um dem vorzubeugen, empfiehlt es sich imo, eine entsprechende LOOP-Anweisung (o.ä.) unmittelbar hinter dem REP?-Block zu pla- zieren.
Bei CPUs ab 386 kann man sich dagegen darauf verlassen, daß die unterbrochene REP?-Anweisung auch korrekt fortgesetzt wird.
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #UD
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | RET - Return from Procedure |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
------------------------------------------------------------------------- RET NP 2 5 10+m 11+m 16 C3
NEAR Return
------------------------------------------------------------------------- RET NP 4 13 18+m 15+m 26 CB PM= PM= PM= 18 32+m 25+m
FAR Return, zu gleichem Privileg
------------------------------------------------------------------------- RET NP 23 33 68 55 - CB
PM, FAR Return, zu niedrigerem Privileg
------------------------------------------------------------------------- RET imm16 NP 3 5 10+m 11+m 20 C2 iw
NEAR Return, imm16 Bytes Stack freigeben
------------------------------------------------------------------------- RET imm16 NP 4 14 18+m 15+m 25 CA iw PM= PM= PM= 17 32+m 25+m
FAR Return, zu gleichem Privileg, imm16 Bytes Stack freigeben
------------------------------------------------------------------------- RET imm16 NP 23 33 68 55 - CB
PM, FAR Return, zu niedrigerem Privileg, imm16 Bytes Stack freigeben
-------------------------------------------------------------------------
Pseudocode:
if NEAR Return then
if OperandSize = 32 then EIP = POPD() else IP = POPW() endif
if imm16 then Inc(eSP, imm16) endif
else
; FAR Return
if ((PE = 0) OR (PE = 1 AND VM = 1)) then
; Real Address oder Virtual Mode
if OperandSize = 32 then EIP = POPD() CS = POPD() AND 0000ffffH else IP = POPW() CS = POPW() endif
if imm16 then Inc(eSP, imm16) endif
else
; reiner Protected Mode
if OperandSize = 32 then if (3rd_word_on_Stack_out_of_Limits) then #SS[0] endif else if (2nd_word_on_Stack_out_of_Limits) then #SS[0] endif endif
if Return_CS_Sel.RPL < CPL then #GP[Return_CS_Sel] endif
if Return_CS_Sel.RPL = CPL then
; same privilege
if OperandSize = 32 then if Return_CS_Sel_at_eSP+4 = 0 then #GP[0] endif else if Return_CS_Sel_at_eSP+2 = 0 then #GP[0] endif endif
if NOT (Return_CS_SelIdx_In_Tbl_Limits) then #GP[Return_CS_Sel] endif
if Access_Rights <> Code_Seg then #GP[Return_CS_Sel] endif
if Non_conforming_Return_CS then if Return_CS_Sel.DPL <> CPL then #GP[Return_CS_Sel] endif endif
if Conforming_Return_CS then if Return_CS_Sel.DPL > CPL then #GP[Return_CS_Sel] endif endif
if (Return_CS_not_present) then #NP[Return_CS_Sel] endif
if (Return_eIP_out_of_Return_CS_Limits) then #GP[0] endif
if OperandSize = 32 then InternalLoad CS:EIP from Stack InternalLoad CS with New_CS_Descriptor Inc(eSP, 8) else InternalLoad CS:IP from Stack InternalLoad CS with New_CS_Descriptor Inc(eSP, 4) endif
if imm16 then Inc(eSP, imm16) endif
else
; less privilege
if OperandSize = 32 then if (Top_16[+imm16]_Bytes_on_stack_out_of_SS_Limits) then #SS[0] endif else if (Top_8[+imm16]_Bytes_on_stack_out_of_SS_Limits) then #SS[0] endif endif
if Return_CS_Sel = 0 then #GP[0] endif
if NOT (Return_CS_SelIdx_In_Tbl_Limits) then #GP[Return_CS_Sel] endif
if Access_Rights <> Code_Seg then #GP[Return_CS_Sel] endif
if Non_conforming_Return_CS then if Return_CS_Sel.DPL <> CPL then #GP[Return_CS_Sel] endif endif
if Conforming_Return_CS then if Return_CS_Sel.DPL > CPL then #GP[Return_CS_Sel] endif endif
if (Return_CS_not_present) then #NP[Return_CS_Sel] endif
if Return_SS_Sel = 0 then #GP[0] endif
if NOT (Return_SS_SelIdx_In_Tbl_Limits) then #GP[Return_SS_Sel] endif
if Return_SS_Sel.RPL <> Return_CS_Sel.RPL then #GP[Return_SS_Sel] endif
if Access_Rights <> Writable_Seg then #GP[Return_SS_Sel] endif
if Return_SS_Sel.DPL <> Return_CS_Sel.RPL then #GP[Return_SS_Sel] endif
if (Return_SS_not_present) then #NP[Return_SS_Sel] endif
if (Return_EIP_out_of_Return_CS_Limits) then #GP[0] endif
if OperandSize = 32 then InternalLoad CS:EIP from stack Return_CS_Sel.RPL = CPL Inc(eSP, 8[+imm16]) else InternalLoad CS:IP from stack Return_CS_Sel.RPL = CPL Inc(eSP, 4[+imm16]) endif
InternalLoad SS:eSP from stack InternalLoad CS with New_CS_Descriptor InternalLoad SS with New_SS_Descriptor
for (ES, FS, GS and DS) do
TmpValid = true
if Seg_SelIdx_out_of_Tbl_Limits then TmpValid = false endif
if Seg_Access_Rights <> (Data_Seg OR Readable_Code_Seg) then TmpValid = false endif
if Seg_is_Data_or_Non_conforming_CS then if ((Seg_DPL < CPL) OR (Seg_DPL < RPL)) then TmpValid = false endif endif
if NOT (TmpValid) Register = 0 Clear_Valid_Flag endif
enddo
endif
endif
endif
Beschreibung:
RET wird i.d.R. zum Beenden einer zuvor mit CALL aufgerufenen Prozedur benutzt. Das Programm wird dadurch mit der dem CALL unmittelbar folgen- den Anweisung fortgesetzt.
Im RM und VM werden durch RET lediglich der vom CALL auf dem Stack ge- sicherte eIP, und falls es ein FAR CALL war, auch das CS zurückgeladen.
Im PM wird bei einem NEAR RET auch nur der eIP vom Stack zurückgeholt, bei FAR RET dagegen geht die übliche Protected Mode Lawine los... ;)
Bei Verwendung einer RET imm16 Variante werden nach dem POPen des eIP noch genau imm16 weitere Bytes (USE16) bzw. Words (USE32) vom Stack ge- holt, um das Handling von Parameterübergaben via Stack zu erleichtern.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #GP[0] (IOPL < 3), #PF[Code], #AC[0] PM #TS, #NP, #SS und #GP (siehe Pseudocode), #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | ROL - Rotate Left |! - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei Einfachrotationen OF = * bei Mehrfachrotationen OF = ?
Befehl Pipe P5 486 386 286 86 Opcode
ROL r|m8, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D0 /0 ROL r|m16, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D1 /0 ROL r|m32, 1 PU 1|3 3|4 3|7 - - D1 /0 ROL r|m8, imm8 PU 1|3 2|4 3|7 5|8 - C0 /0 ib ROL r|m16, imm8 PU 1|3 2|4 3|7 5|8 - C1 /0 ib ROL r|m32, imm8 PU 1|3 2|4 3|7 - - C1 /0 ib ROL r|m8, CL NP 4 3|4 3|7 5|8 8+4n| D2 /0 20+4n+EA ROL r|m16, CL NP 4 3|4 3|7 5|8 8+4n| D3 /0 20+4n+EA ROL r|m32, CL NP 4 3|4 3|7 - - D3 /0
Pseudocode:
Count = SRC
while Count <> 0 do TmpCF = DEST[MSB] DEST = (DEST SHL 1) + TmpCF Dec(Count) endwhile
CF = TmpCF if SRC = 1 then OF = DEST[MSB] XOR CF else OF = ? endif
Beschreibung:
ROL rotiert DEST (8, 16 oder 32 Bits (exkl. CF)) SRC mal nach links. Bei jedem Durchlauf merkt sich die CPU den Inhalt des MSB und verschiebt da- nach DEST um eine Stelle nach links (wodurch das MSB im CF landet). Ab- schließend wird das gemerkte MSB ins LSB kopiert:
+------- DEST --------+ +--+ +---+---+- : -+---+---+ |CF|<-+--|MSB| | : | |LSB|<-+ +--+ | +---+---+- : -+---+---+ | +---------------------------+
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Rotationsanzahl wird automatisch auf 0..31 begrenzt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | ROR - Rotate Right |! - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei Einfachrotationen OF = * bei Mehrfachrotationen OF = ?
Befehl Pipe P5 486 386 286 86 Opcode
ROR r|m8, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D0 /1 ROR r|m16, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D1 /1 ROR r|m32, 1 PU 1|3 3|4 3|7 - - D1 /1 ROR r|m8, imm8 PU 1|3 2|4 3|7 5|8 - C0 /1 ib ROR r|m16, imm8 PU 1|3 2|4 3|7 5|8 - C1 /1 ib ROR r|m32, imm8 PU 1|3 2|4 3|7 - - C1 /1 ib ROR r|m8, CL NP 4 3|4 3|7 5|8 8+4n| D2 /1 20+4n+EA ROR r|m16, CL NP 4 3|4 3|7 5|8 8+4n| D3 /1 20+4n+EA ROR r|m32, CL NP 4 3|4 3|7 - - D3 /1
Pseudocode:
Count = SRC
while Count <> 0 do TmpCF = DEST[LSB] DEST = (DEST SHR 1) + (TmpCF SHL (OperandSize-1)) Dec(Count) endwhile
CF = TmpCF if SRC = 1 then OF = DEST[MSB] XOR DEST[MSB-1] else OF = ? endif
Beschreibung:
ROR rotiert DEST (8, 16 oder 33 Bits (exkl. CF)) SRC mal nach rechts. Bei jedem Durchlauf merkt sich die CPU zuerst das LSB, verschiebt danach DEST um eine Stelle nach rechts (wodurch das LSB von DEST in CF landet) und fügt abschließend das gemerkte LSB wieder in das MSB von DEST ein:
+------- DEST --------+ +---+---+- : -+---+---+ +--+ +->|MSB| | : | |LSB|--+->|CF| | +---+---+- : -+---+---+ | +--+ +---------------------------+
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Rotationsanzahl wird automatisch auf 0..31 begrenzt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | RSM - Resume from SMM |* * * * * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
RSM NP 83 - - - - 0F AA
Pseudocode:
-
Beschreibung:
RSM bewirkt die Rückkehr in den beim Eintritt in den SMM (System Manag- ment Mode) gesicherten Prozessormodus (RM, VM, oder PM) und -status.
System- und Anwendungsprogrammierern dürfte der SMM imo herzlich egal ;) sein, da dieser Modus nur über ein externes Signal am SMI# Pin aktiviert werden kann und dadurch Hardware-Bastlern lediglich eine Klapptür zum Power Managment öffnet... 8)
HINWEIS: Der Versuch RSM auszuführen, wenn sich die CPU nicht im SMM be- findet, endet kläglich mit Auslösung von Interrupt 6 (#UD, In- valid Opcode)... ;)
HINWEIS: Stellt die CPU bei der Rückkehr irgendeine ungültige Statusin- formation fest, geht sie sofort in den ShutdownMode. Dies pas- siert z.B. dann, wenn durch die gesicherten Informationen eines der reservierten Bits von CR4, oder eine illegale Kombination von Bits in CR0 gesetzt würde (PE=0 & PG=1, oder CD=0 & NW=1).
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #UD
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SAHF - Store AH into FLAGS |- - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SAHF NP 2 2 3 2 4 9E
Pseudocode:
SF:ZF:xx:AF:xx:PF:xx:CF = AH
Beschreibung:
SAHF lädt das LowByte des FLAGS-Registers mit dem Wert von AH. SAHF ist das Gegenstück zu LAHF und wird überwiegend dazu verwendet, einen zuvor mit LAHF gesicherten Flagzustand wieder zu restaurieren, oder um impli- zit eine gewünschte Flag-Kombination zu setzen.
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SAL - Shift Arithmetic Left |! - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei Einfachrotationen OF = * bei Mehrfachrotationen OF = ?
Befehl Pipe P5 486 386 286 86 Opcode
SAL r|m8, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D0 /4 SAL r|m16, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D1 /4 SAL r|m32, 1 PU 1|3 3|4 3|7 - - D1 /4 SAL r|m8, imm8 PU 1|3 2|4 3|7 5|8 - C0 /4 ib SAL r|m16, imm8 PU 1|3 2|4 3|7 5|8 - C1 /4 ib SAL r|m32, imm8 PU 1|3 2|4 3|7 - - C1 /4 ib SAL r|m8, CL NP 4 3|4 3|7 5|8 8+4n| D2 /4 20+4n+EA SAL r|m16, CL NP 4 3|4 3|7 5|8 8+4n| D3 /4 20+4n+EA SAL r|m32, CL NP 4 3|4 3|7 - - D3 /4
Pseudocode:
Count = SRC
while Count <> 0 do CF = DEST[MSB] DEST = (DEST SHL 1) DEST[LSB] = 0 Dec(Count) endwhile
if SRC = 1 then OF = DEST[MSB] XOR CF else OF = ? endif
Beschreibung:
SAL verschiebt DEST um SRC Bits nach links. Bei jedem Durchlauf wird das MSB dabei ins CF geschoben und das LSB gelöscht:
+------- DEST --------+ +--+ +---+---+- : -+---+---+ +-+ |CF|<----|MSB| | : | |LSB|<---|0| +--+ +---+---+- : -+---+---+ +-+
HINWEIS: SAL und SHL bzw. ihre jeweiligen Varianten besitzen identische Opcodes und werden von den Assemblerpaketen entsprechend gleich behandelt. Alle mir bekannten Debugger reassemblieren die Op- codes jedoch immer zu SHL Mnemonics. Leute, die nach ihren SAL- Anweisungen suchen, dürften das somit wohl endlos tun... ;)
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Verschiebungsanzahl wird automatisch auf 0..31 begrenzt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SAR - Shift Arithmetic Right |! - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei Einfachrotationen OF = 0 bei Mehrfachrotationen OF = ?
Befehl Pipe P5 486 386 286 86 Opcode
SAR r|m8, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D0 /7 SAR r|m16, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D1 /7 SAR r|m32, 1 PU 1|3 3|4 3|7 - - D1 /7 SAR r|m8, imm8 PU 1|3 2|4 3|7 5|8 - C0 /7 ib SAR r|m16, imm8 PU 1|3 2|4 3|7 5|8 - C1 /7 ib SAR r|m32, imm8 PU 1|3 2|4 3|7 - - C1 /7 ib SAR r|m8, CL NP 4 3|4 3|7 5|8 8+4n| D2 /7 20+4n+EA SAR r|m16, CL NP 4 3|4 3|7 5|8 8+4n| D3 /7 20+4n+EA SAR r|m32, CL NP 4 3|4 3|7 - - D3 /7
Pseudocode:
Count = SRC TmpCF = DEST[MSB]
while Count <> 0 do DEST = (DEST SHR 1) DEST[MSB] = TmpCF Dec(Count) endwhile
if SRC = 1 then OF = 0 else OF = ? endif
Beschreibung:
SAR verschiebt DEST um SRC Bits nach rechts. Bei jedem Durchlauf wird zunächst das MSB temporär gesichert, danach DEST um ein Bit nach rechts verschoben (wodurch das LSB im CF landet), und abschließend das MSB aus der temporären Sicherung wiederhergestellt (dadurch bleibt das Vorzeich- en erhalten):
+------- DEST --------+ +---+---+- : -+---+---+ +--+ +->|MSB| | : | |LSB|---->|CF| | +---+---+- : -+---+---+ +--+ +----+
SAR entspricht im Prinzip einer schnelleren Variante von IDIV. Aller- dings ist der Divisor bei SAR immer automatisch auf ein Vielfaches von 2 fixiert und es wird anders gerundet (beide runden zwar ab, IDIV aber ge- gen Null, SAR gegen negative Unendlichkeit). Der Rundungsunterschied ma- cht sich folglich nur bei negativen Dividenden bemerkbar (Beispiel -5/2: Quotient bei IDIV = -2, bei SAR = -3).
HINWEIS: In manchen Dokumentationen wird SAR als Synonym für SHR bezei- chnet, wohl in der Annahme, daß das, was für SAL und SHL gilt, auch für SAR und SHR gelten muß. Dies ist definitiv falsch. SAR und SHR bzw. deren Varianten besitzen unterschiedliche Opcodes und unterscheiden sich auch in ihrer Funktion (bei SARs bleibt der Wert des ursprünglichen MSBs (Sign) erhalten, bei SHRs wird es nach rechts verschoben).
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Verschiebungsanzahl wird automatisch auf 0..31 begrenzt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SBB - Subtract with Borrow |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SBB AL, imm8 PU 1 1 2 3 4 1C ib SBB AX, imm16 PU 1 1 2 3 4 1D iw SBB EAX, imm32 PU 1 1 2 - - 1D id SBB r|m8, imm8 PU 1|3 1|3 2|7 3|7 4|17+EA 80 /3 ib SBB r|m16, imm16 PU 1|3 1|3 2|7 3|7 4|17+EA 81 /3 iw SBB r|m32, imm32 PU 1|3 1|3 2|7 - - 81 /3 id SBB r|m16, imm8 PU 1|3 1|3 2|7 3|7 4|17+EA 83 /3 ib SBB r|m32, imm8 PU 1|3 1|3 2|7 - - 83 /3 ib SBB r|m8, r8 PU 1|3 1|3 2|7 2|7 3|16+EA 18 /r SBB r|m16, r16 PU 1|3 1|3 2|7 2|7 3|16+EA 19 /r SBB r|m32, r32 PU 1|3 1|3 2|7 - - 19 /r SBB r8, r|m8 PU 1|2 1|2 2|6 2|7 3|9+EA 1A /r SBB r16, r|m16 PU 1|2 1|2 2|6 2|7 3|9+EA 1B /r SBB r32, r|m32 PU 1|2 1|2 2|6 - - 1B /r
Pseudocode:
DEST = DEST - SRC - CF
Beschreibung:
Subtrahiert SRC und den Inhalt des CF, den das CF VOR Ausführung von SBB hatte, von DEST und legt das Ergebnis in DEST ab. Beispiel:
mov dx, 1 xor ax, ax ; DX:AX = 0001:0000H sub ax, 3 sbb dx, 0 =================== DX:AX = 0000:fffdH
Ist DEST ein Word oder DWord und SRC ein imm8, wird SRC von der CPU vor der Addition automatisch vorzeichenbehaftet auf Word/DWord erweitert.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SCAS? - Scan String Operand |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SCASB NP 4 6 7 7 15 AE SCASW NP 4 6 7 7 15 AF SCASD NP 4 6 7 - - AF SCAS m8 NP 4 6 7 7 15 AE SCAS m16 NP 4 6 7 7 15 AF SCAS m32 NP 4 6 7 - - AF
Pseudocode:
if AddrSize = 16 then DESTIdx = DI else DESTIdx = EDI endif
case OperandSize of
Byte
if DF = 0 then IncDec = 1 else IncDec = -1 endif FLAGS for AL - [byte ptr ES:DESTIdx]
Word
if DF = 0 then IncDec = 2 else IncDec = -2 endif FLAGS for AX - [word ptr ES:DESTIdx]
DWord
if DF = 0 then IncDec = 4 else IncDec = -4 endif FLAGS for EAX - [dword ptr ES:DESTIdx]
endcase
DESTIdx = DESTIdx + IncDec
Beschreibung:
SCAS? vergleicht das Byte, Word oder DWord (AL, AX, EAX), mit dem Byte, Word oder DWord auf das ES:DESTIdx zeigt (kein Segment Override möglich) und setzt die FLAGS entsprechend.
Abschließend wird in Abhängigkeit vom DF-Zustand DESTIdx um 1, 2 od. 4 (Byte, Word oder DWord) in- (DF=0) bzw. dekrementiert (DF=1).
DF ist z.B. über die Instructions CLD (DF=0) bzw. STD (DF=1) steuerbar.
Im Zusammenhang mit REP? Prefixes und dem eCX-Register können auch ganze Byte-, Word- oder DWord-Strings durchsucht werden.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SETALC - Set AL to CF |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
± SETALC NP 3 3 3 3 - D6
Pseudocode:
if CF = 1 then AL = ffH else AL = 00H endif
Beschreibung:
Dieser Opcode (d6H) existiert zwar bereits seit den 286 CPUs, wird von Intel aber aus unerfindlichen Gründen bis heute (einschl. PPro-Dokus) verschwiegen... ;)
Da er AL in Abhängigkeit des momentanen Wertes des CF setzt, hat sich in Programmiererkreisen SETALC durchgesetzt (ein offizielles Mnemonic exi- stiert ja bekanntlich nicht).
Mit dem 386er wurde zwar das offizielle Mnemonic SETC r|m8 eingeführt, mit dem sich ein ähnlicher Effekt erzielen lässt (AL = 1, wenn CF = 1), aber viele Programmierer benutzen für CPUs bis einschließlich 486 wei- terhin bevorzugt SETALC, da es bis zu 33% schneller als die offizielle Variante ist.
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SETcc - Set Byte if Condition Code is met |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SETA r|m8 NP 1|2 4|3 4|5 - - 0F 97 SETAE r|m8 NP 1|2 4|3 4|5 - - 0F 93 SETB r|m8 NP 1|2 4|3 4|5 - - 0F 92 SETBE r|m8 NP 1|2 4|3 4|5 - - 0F 96 SETC r|m8 NP 1|2 4|3 4|5 - - 0F 92 SETE r|m8 NP 1|2 4|3 4|5 - - 0F 94 SETG r|m8 NP 1|2 4|3 4|5 - - 0F 9F SETGE r|m8 NP 1|2 4|3 4|5 - - 0F 9D SETL r|m8 NP 1|2 4|3 4|5 - - 0F 9C SETLE r|m8 NP 1|2 4|3 4|5 - - 0F 9E SETNA r|m8 NP 1|2 4|3 4|5 - - 0F 96 SETNAE r|m8 NP 1|2 4|3 4|5 - - 0F 92 SETNB r|m8 NP 1|2 4|3 4|5 - - 0F 93 SETNBE r|m8 NP 1|2 4|3 4|5 - - 0F 97 SETNC r|m8 NP 1|2 4|3 4|5 - - 0F 93 SETNE r|m8 NP 1|2 4|3 4|5 - - 0F 95 SETNG r|m8 NP 1|2 4|3 4|5 - - 0F 9E SETNGE r|m8 NP 1|2 4|3 4|5 - - 0F 9C SETNL r|m8 NP 1|2 4|3 4|5 - - 0F 9D SETNLE r|m8 NP 1|2 4|3 4|5 - - 0F 9F SETNO r|m8 NP 1|2 4|3 4|5 - - 0F 91 SETNP r|m8 NP 1|2 4|3 4|5 - - 0F 9B SETNS r|m8 NP 1|2 4|3 4|5 - - 0F 99 SETNZ r|m8 NP 1|2 4|3 4|5 - - 0F 95 SETO r|m8 NP 1|2 4|3 4|5 - - 0F 90 SETP r|m8 NP 1|2 4|3 4|5 - - 0F 9A SETPE r|m8 NP 1|2 4|3 4|5 - - 0F 9A SETPO r|m8 NP 1|2 4|3 4|5 - - 0F 9B SETS r|m8 NP 1|2 4|3 4|5 - - 0F 98 SETZ r|m8 NP 1|2 4|3 4|5 - - 0F 94
Pseudocode:
if (ConditionCode) then DEST = 1 else DEST = 0 endif
Beschreibung:
Alle SETcc-Instructions prüfen, ob bestimmte Bits oder Bitkombinationen im FLAGS-Register momentan gesetzt und/oder gelöscht sind. Wird die ge- wünschte Bedingung (Condition Code) erfüllt, wird DEST = 1, anderenfalls DEST = 0 gesetzt.
SETcc-Instructions werden überwiegend nach CMP-Instructions angewendet, um das Ergebnis des Vergleiches zu interpretieren und festzuhalten.
SETA Set if Above, (CF=0 AND ZF=0), ">", unsigned SETAE Set if Above or Equal, (CF=0), ">=", unsigned SETB Set if Below, (CF=1), "<", unsigned SETBE Set if Below or Equal, (CF=1 AND ZF=1), "<=", unsigned SETC Set if Carry, (CF=1) SETE Set if Equal, (ZF=1), "=" SETG Set if Greater, (ZF=0 AND SF=OF), ">", signed SETGE Set if Greater or Equal, (SF=OF), ">=", signed SETL Set if Less, (SF<>OF), "<", signed SETLE Set if Less or Equal, (ZF=1 AND SF<>OF), "<=", signed SETNA Set if Not Above, (CF=1 OR ZF=1), "!>", unsigned SETNAE Set if Not Above or Equal, (CF=1), "!>=", unsigned SETNB Set if Not Below, (CF=0), "!<", unsigned SETNBE Set if Not Below or Equal, (CF=0 AND ZF=0), "!<=", unsigned SETNC Set if Not Carry, (CF=0) SETNE Set if Not Equal, (ZF=0), "<>" SETNG Set if Not Greater, (ZF=1 OR SF<>OF), "!>", signed SETNGE Set if Not Greater or Equal, (SF<>OF), "!>=", signed SETNL Set if Not Less, (SF=OF), "!<", signed SETNLE Set if Not Less or Equal, (ZF=0 AND SF=OF), "!<=", signed SETNO Set if Not Overflow, (OF=0) SETNP Set if Not Parity, (PF=0) SETNS Set if Not Sign, (SF=0) SETNZ Set if Not Zero, (ZF=0), "<>" SETO Set if Overflow, (OF=1) SETP Set if Parity, (PF=1) SETPE Set if Parity Even, (PF=1) SETPO Set if Parity Odd, (PF=0) SETS Set if Sign, (SF=1) SETZ Set if Zero, (ZF=1), "="
Wie üblich gilt auch hier, daß die Begriffe "Greater" und "Less" für Vergleiche von vorzeichenbehafteten (signed) Werten, und die Begriffe "Above" und "Below" dagegen für Byte-, Word- und DWord-Vergleiche (un- signed) gedacht sind.
HINWEIS: Einige Hochsprachen interpretieren boolesche Variablen nur dann als TRUE oder logisch 1, wenn das Byte den Wert ffH hat, SETcc setzt aber bekanntlich nur 00H oder 01H.
Um auf das richtige Format zu kommen, empfiehlt sich aus Gesch- windigkeitsgründen das Prüfen auf die entgegengesetzte Beding- ung und nachfolgendes Dekrementieren (aus 00H wird ffH, aus 01H wird 00H).
Ist bei einer Prüfung nur das CF interessant, ist eher der Ein- satz von Opcode d6H zu empfehlen (zumindest auf 386/486 CPUs). Dieser Opcode ist zwar undokumentiert (siehe SETALC), existiert aber bereits seit den 286ern und ist bis zu 100% schneller als eine entsprechende SETcc/DEC Sequenz.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SGDT - Store Global Descriptor Table Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SGDT m16&32 NP 4 10 9 11 - 0F 01 /0
Pseudocode:
if m16&32 = Type_of_register then
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
#UD[0]
else
; RM oder VM
Int 6
endif
endif
[word ptr m16&32] = GDTR.Limit
TmpBase = GDTR.Base if OperandSize = 16 then if CPU > 286 then TmpBase = TmpBase AND 00ffffffH endif endif [dword ptr m16&32+2] = TmpBase
Beschreibung:
Mit SGDT lassen sich Base und Limit aus dem Descriptor Cache des GDTR auslesen und in DEST abspeichern.
SGDT wird üblicherweise zur Initialisierung von PM-Umgebungen bei Kalt- und/oder Warmstarts eingesetzt, und daher i.d.R. nur von Betriebssystem- en verwendet. Allerdings ist SGDT im Gegensatz LGDT keine privilegierte Instruction, sie kann also auch problemlos von Anwendungsprogrammen be- nutzt werden... ;)
Der Versuch ein Register als DEST einzusetzen, wird gnadenlos mit einer Exception 6 (#UD, Invalid Opcode) bestraft... &)
HINWEIS: Mit SGDT läßt übrigens auch prüfen, ob eine 286er CPU vorhanden ist. 286er CPUs verfügen nur über 24 Adressleitungen, was dazu führt, daß die obersten 8 Bit von GDTR.Base nutzlos sind. Aus diesem Grund ist auf 286ern das oberste Byte in GDTR.Base auch immer = ffH (siehe dazu auch Topic LSG0007).
HINWEIS: In einigen Dokumentationen wird fälschlicherweise behauptet, daß SGDT zu den privilegierten Instructions zählt. Dem ist na- türlich nicht so... ;)
Exceptions:
RM Int 6 (Invalid), Int 13 (Wrap) VM Int 6 (Invalid), Int 13 (Wrap), #PF[Code], #AC[0] PM #UD, #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SHL - Shift Left |! - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei Einfachrotationen OF = * bei Mehrfachrotationen OF = ?
Befehl Pipe P5 486 386 286 86 Opcode
SHL r|m8, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D0 /4 SHL r|m16, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D1 /4 SHL r|m32, 1 PU 1|3 3|4 3|7 - - D1 /4 SHL r|m8, imm8 PU 1|3 2|4 3|7 5|8 - C0 /4 ib SHL r|m16, imm8 PU 1|3 2|4 3|7 5|8 - C1 /4 ib SHL r|m32, imm8 PU 1|3 2|4 3|7 - - C1 /4 ib SHL r|m8, CL NP 4 3|4 3|7 5|8 8+4n| D2 /4 20+4n+EA SHL r|m16, CL NP 4 3|4 3|7 5|8 8+4n| D3 /4 20+4n+EA SHL r|m32, CL NP 4 3|4 3|7 - - D3 /4
Pseudocode:
Count = SRC
while Count <> 0 do CF = DEST[MSB] DEST = (DEST SHL 1) DEST[LSB] = 0 Dec(Count) endwhile
if SRC = 1 then OF = DEST[MSB] XOR CF else OF = ? endif
Beschreibung:
SHL verschiebt DEST um SRC Bits nach links. Bei jedem Durchlauf wird das MSB dabei ins CF geschoben und das LSB gelöscht:
+------- DEST --------+ +--+ +---+---+- : -+---+---+ +-+ |CF|<----|MSB| | : | |LSB|<---|0| +--+ +---+---+- : -+---+---+ +-+
HINWEIS: SHL und SAL bzw. ihre jeweiligen Varianten besitzen identische Opcodes und werden von den Assemblerpaketen entsprechend gleich behandelt. Alle mir bekannten Debugger reassemblieren die Op- codes jedoch immer zu SHL Mnemonics.
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Verschiebungsanzahl wird automatisch auf 0..31 begrenzt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SHLD - Shift Left Double Precision |! - - - ! ! ! ! !| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei ShiftCount = 0
CF, PF, AF, ZF, SF, OF ! = -
bei ShiftCount > 0 AND < (SizeOf(DEST))
CF, PF, ZF, SF ! = * AF ! = ? OF ! = * (bei ShiftCount = 1) OF ! = ? (bei ShiftCount > 1)
bei ShiftCount > SizeOf(DEST)
CF, PF, AF, ZF, SF, OF ! = ?
Befehl Pipe P5 486 386 286 86 Opcode
SHLD r|m16, r16, imm8 NP 4 2|3 3|7 - - 0F A4 SHLD r|m32, r32, imm8 NP 4 2|3 3|7 - - 0F A4 SHLD r|m16, r16, CL NP 4|5 3|4 3|7 - - 0F A5 SHLD r|m32, r32, CL NP 4|5 3|4 3|7 - - 0F A5
Pseudocode:
ShiftCount = 3rd_operand TmpSRC = SRC
if ShiftCount > 0 then
if ShiftCount < SizeOf(DEST) then
while ShiftCount > 0 do
CF = DEST[MSB] DEST = DEST SHL 1
TmpCF = TmpSRC[MSB] TmpSRC = TmpSRC SHL 1
DEST[LSB] = TmpCF
Dec(ShiftCount)
endwhile
if 3rd_Operand = 1 then OF = DEST[MSB] XOR CF else OF = ? endif
AF = ?
else
; ShiftCount ist > 16, DEST aber nur 16 Bits
CF = ? PF = ? AF = ? ZF = ? SF = ? OF = ?
endif
endif
Beschreibung:
SHLD verschiebt DEST um COUNT Bits nach links. Bei jedem Durchlauf wird dabei das MSB von DEST ins CF geschoben. Danach wird SRC ebenfalls um ein Bit nach links geschoben (nur temporär, SRC wird durch SHLD nicht tatsächlich verändert), und das LSB von DEST durch das herausgeschobene MSB von SRC ersetzt:
+-- DEST (r|m16/32) --+ +--- SRC (r16/32) ----+ +--+ +---+---+- : -+---+---+ +---+---+- : -+---+---+ |CF|<----|MSB| | : | |LSB|<----|MSB| | : | |LSB| +--+ +---+---+- : -+---+---+ +---+---+- : -+---+---+
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Verschiebungsanzahl wird automatisch auf 0..31 begrenzt.
HINWEIS: Ist der ShiftCount größer als der Operand, also ShiftCount > 16 und DEST nur ein Word (16bit) wird KEIN Shift ausgeführt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SHR - Shift Right |! - - - - - - - *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei Einfachrotationen OF = 0 bei Mehrfachrotationen OF = ?
Befehl Pipe P5 486 386 286 86 Opcode
SHR r|m8, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D0 /5 SHR r|m16, 1 PU 1|3 3|4 3|7 2|7 2|15+EA D1 /5 SHR r|m32, 1 PU 1|3 3|4 3|7 - - D1 /5 SHR r|m8, imm8 PU 1|3 2|4 3|7 5|8 - C0 /5 ib SHR r|m16, imm8 PU 1|3 2|4 3|7 5|8 - C1 /5 ib SHR r|m32, imm8 PU 1|3 2|4 3|7 - - C1 /5 ib SHR r|m8, CL NP 4 3|4 3|7 5|8 8+4n| D2 /5 20+4n+EA SHR r|m16, CL NP 4 3|4 3|7 5|8 8+4n| D3 /5 20+4n+EA SHR r|m32, CL NP 4 3|4 3|7 - - D3 /5
Pseudocode:
Count = SRC
while Count <> 0 do CF = DEST[LSB] DEST = (DEST SHR 1) DEST[MSB] = 0 Dec(Count) endwhile
if SRC = 1 then OF = 0 else OF = ? endif
Beschreibung:
SHR verschiebt DEST um SRC Bits nach rechts. Bei jedem Durchlauf wird DEST um ein Bit nach rechts verschoben (wodurch das LSB im CF landet), und abschließend das MSB gelöscht:
+------- DEST --------+ +-+ +---+---+- : -+---+---+ +--+ |0|---->|MSB| | : | |LSB|---->|CF| +-+ +---+---+- : -+---+---+ +--+
SHR eignet sich für schnelle Divisionen von vorzeichenlosen (unsigned) Zahlen, vorausgesetzt, daß ein Vielfaches von 2 als Divisor erwünscht ist (jeder Shift entspricht hier einer Division durch 2).
HINWEIS: In manchen Dokumentationen wird als Synonym zu SHR das Menmonic SAR genannt. Dies ist definitiv falsch. SHR und SAR bzw. deren Varianten besitzen unterschiedliche Opcodes und unterscheiden sich zudem in ihrer Funktion (bei SARs bleibt der Wert des ur- sprünglichen MSBs (Sign) erhalten, bei SHR wird es gelöscht).
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Verschiebungsanzahl wird automatisch auf 0..31 begrenzt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SHRD - Shift Right Double Precision |! - - - ! ! ! ! !| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
! bei ShiftCount = 0
CF, PF, AF, ZF, SF, OF ! = -
bei ShiftCount > 0 AND < (SizeOf(DEST))
CF, PF, ZF, SF ! = * AF ! = ? OF ! = * (bei ShiftCount = 1) OF ! = ? (bei ShiftCount > 1)
bei ShiftCount > SizeOf(DEST)
CF, PF, AF, ZF, SF, OF ! = ?
Befehl Pipe P5 486 386 286 86 Opcode
SHRD r|m16, r16, imm8 NP 4 2|3 3|7 - - 0F AC SHRD r|m32, r32, imm8 NP 4 2|3 3|7 - - 0F AC SHRD r|m16, r16, CL NP 4|5 3|4 3|7 - - 0F AD SHRD r|m32, r32, CL NP 4|5 3|4 3|7 - - 0F AD
Pseudocode:
ShiftCount = 3rd_operand TmpSRC = SRC
if ShiftCount > 0 then
if ShiftCount < SizeOf(DEST) then
while ShiftCount > 0 do
CF = DEST[LSB] DEST = DEST SHR 1
TmpCF = TmpSRC[LSB] TmpSRC = TmpSRC SHR 1
DEST[MSB] = TmpCF
Dec(ShiftCount)
endwhile
if 3rd_Operand = 1 then OF = DEST[MSB] XOR CF else OF = ? endif
AF = ?
else
; ShiftCount ist > 16, DEST aber nur 16 Bits
CF = ? PF = ? AF = ? ZF = ? SF = ? OF = ?
endif
endif
Beschreibung:
SHRD verschiebt DEST um COUNT Bits nach rechts. Bei jedem Durchlauf wird dabei das LSB von DEST ins CF geschoben. Danach wird SRC ebenfalls um ein Bit nach rechts geschoben (nur temporär, SRC wird durch SHRD nicht tatsächlich verändert), und das MSB von DEST durch das herausgeschobene LSB von SRC ersetzt:
+--- SRC (r16/32) ----+ +-- DEST (r|m16/32) --+ +---+---+- : -+---+---+ +---+---+- : -+---+---+ +--+ |MSB| | : | |LSB|---->|MSB| | : | |LSB|---->|CF| +---+---+- : -+---+---+ +---+---+- : -+---+---+ +--+
HINWEIS: SRC wird ab 386+ CPUs vor der Ausführung mit 01fH geANDet, d.h. die Verschiebungsanzahl wird automatisch auf 0..31 begrenzt.
HINWEIS: Ist der ShiftCount größer als der Operand, also ShiftCount > 16 und DEST nur ein Word (16bit) wird KEIN Shift ausgeführt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SIDT - Store Interrupt Descriptor Table Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SIDT m16&32 NP 4 10 9 12 - 0F 01 /1
Pseudocode:
if m16&32 = Type_of_register then
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
#UD[0]
else
; RM oder VM
Int 6
endif
endif
[word ptr m16&32] = IDTR.Limit
TmpBase = IDTR.Base if OperandSize = 16 then if CPU > 286 then TmpBase = TmpBase AND 00ffffffH endif endif [dword ptr m16&32+2] = TmpBase
Beschreibung:
Mit SIDT lassen sich Base und Limit aus dem Descriptor Cache des IDTR auslesen und in DEST abspeichern.
SIDT wird üblicherweise zur Initialisierung von PM-Umgebungen bei Kalt- und/oder Warmstarts eingesetzt, und daher i.d.R. nur von Betriebssystem- en verwendet.
HINWEIS: Einige Dokumentationen besagen, daß SIDT zu den privilegierten Instructions zählt. Das ist definitiv falsch.
Exceptions:
RM Int 6 (Invalid), Int 13 (Wrap) VM Int 6 (Invalid), Int 13 (Wrap), #PF[Code], #AC[0] PM #UD, #SS[0], #GP[0], #PF[Code], #AC[0]
PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SLDT - Store Local Descriptor Table Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SLDT r|m16 NP 2 2|3 2|3 2|3 - 0F 00 /0 PM= 2|2 SLDT r|m32 NP 2 2|3 2|3 - - 0F 00 /0 PM= 2|2
Pseudocode:
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
else
; RM oder VM
Int 6
endif
DEST = LDTR
Beschreibung:
Mit SLDT lässt sich das LDTR auslesen und in DEST speichern. Das LDTR ist zwar auch ein Descriptor Cache ähnlich dem GDTR, aber SLDT speichert im Gegensatz zu SGDT nicht Base und Limit aus dem Cache, sondern ledig- lich einen Selector zu einem LDT-Descriptor im GDT.
SLDT wird nur im PM erkannt und üblicherweise nur von Betriebssystemen verwendet.
HINWEIS: Bei Einsatz der 32bit-Variante von SLDT ist das Hi-Word in DEST nach der Ausführung natürlich undefiniert. Ein Selector ist nun mal nur 16bit groß... ;)
HINWEIS: In einigen Dokumentationen wird fälschlicherweise behauptet, daß SLDT zu den privilegierten Instructions zählt. Nope... 8)
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #UD, #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SMSW - Store Machine Status Word |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SMSW r|m16 NP 4 2|3 2|3 2|3 - 0F 01 /4 PM= 2|2 SMSW r|m32 NP 4 2|3 2|3 - - 0F 01 /4 PM= 2|2
Pseudocode:
DEST = MSW
Beschreibung:
SMSW speichert den Inhalt des MSW (Machine Status Word, s. BAW0007) in DEST ab.
HINWEIS: Ab 80386 CPUs ist das MSW Bestandteil von CR0, d.h. bei Zugriff auf das MSW via LMSW oder SMSW werden in der Realität die Bits 0..15 von CR0 adressiert. Dem Programmierer kann diese interne Umleitung herzlich egal sein ;), LMSW und SMSW verhalten sich nach außen genau wie zu Zeiten des 80286, d.h. immer noch als unprivilegierte Instructions (im PM kein CPL0 zur Ausführung erforderlich).
HINWEIS: Bei Einsatz der 32bit-Variante von SMSW ist das Hi-Word in DEST auch auf 386+ nach der Ausführung undefiniert. Schade... ;)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | STC - Set Carry Flag |- - - - - - - - 1| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
STC NP 2 2 2 2 2 F9
Pseudocode:
CF = 1
Beschreibung:
Setzt das CF.
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | STD - Set Direction Flag |- 1 - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
STD NP 2 2 2 2 2 FD
Pseudocode:
DF = 1
Beschreibung:
Setzt das DF. STD beeinflusst das Verhalten aller String-Instructions (LODS, STOS, MOVS, CMPS, SCAS, INS, OUTS). Nach einem STD werden die betroffenen Indexregister (eSI und/oder eDI) fortan dekrementiert.
Exceptions:
RM - VM - PM -
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | STI - Set Interrupt Flag (Enable) |- - 1 - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
STI NP 7 5 3 3 2 FB
Pseudocode:
if PE = 1 then if VM = 0 then if CPL > IOPL then #GP[0] endif else if IOPL <> 3 then #GP[0] endif endif endif
IF = 1
Beschreibung:
Setzt das IF. Dadurch werden alle über den PIC maskierbaren Hardware- Interrupts (IRQ0..IRQ15) von ihren ISRs wieder bedient.
Im RM ist STI immer möglich, im VM nur bei IOPL3 und im PM solange CPL kleiner oder gleich IOPL ist.
HINWEIS: Die ISRs bedienen ihre IRQs erst dann wieder, nachdem die dem STI unmittelbar folgende Instruction ausgeführt wurde. Sinn des ganzen ist, daß IRQs am Ende einer Prozedur wieder freigegeben werden können, diese aber vor der Rückkehr aus der Prozedur (durch RET/RETF) noch nicht von ihren ISRs bedient werden. Das ist manchmal ganz hilfreich, je nach Programm...
Tückisch wird dieses Verhalten bei STI/CLI Sequenzen, also wenn man in zeitkritischen Routinen nur kurz ein IRQ-Fenster öffnen möchte, um aufgelaufenen IRQs eine Chance zu geben. Folgt das CLI unmittelbar auf das STI, hat CLI nämlich das IF wieder ge- löscht, bevor die eigentliche IRQ-Freigabe durch STI anstände. Folglich würde keine IRQ-ISR auch nur einen Finger in die Tür bekommen. Und die Moral von der Geschicht':
Mach' kein STI/CLI ohne NOP in der Mitte nicht... ;)
Exceptions:
RM - VM #GP[0] PM #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | STOS? - Store to String Operand |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
STOSB NP 3 5 4 3 11 AA STOSW NP 3 5 4 3 11 AB STOSD NP 3 5 4 - - AB STOS m8 NP 3 5 4 3 11 AA STOS m16 NP 3 5 4 3 11 AB STOS m32 NP 3 5 4 - - AB
Pseudocode:
if AddrSize = 16 then DESTIdx = DI else DESTIdx = EDI endif
case OperandSize of
Byte
if DF = 0 then IncDec = 1 else IncDec = -1 endif [byte ptr ES:DESTIdx] = AL
Word
if DF = 0 then IncDec = 2 else IncDec = -2 endif [word ptr ES:DESTIdx] = AX
DWord
if DF = 0 then IncDec = 4 else IncDec = -4 endif [dword ptr DESTIdx] = EAX
endcase
DESTIdx = DESTIdx + IncDec
Beschreibung:
STOS? speichert das Byte, Word oder DWord, aus AL, AX, oder EAX an der Adresse, auf die ES:DESTIdx zeigt (kein Segment Override möglich).
Abschließend wird in Abhängigkeit vom DF-Zustand DESTIdx um 1, 2 oder 4 (Byte, Word oder DWord) in- (DF=0) bzw. dekrementiert (DF=1).
DF ist z.B. über die Instructions CLD (DF=0) bzw. STD (DF=1) steuerbar.
Im Zusammenhang mit REP? Prefixes und dem eCX-Register können auch ganze Byte-, Word- oder DWord-Strings gefüllt werden.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | STR - Store Task Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
STR r|m16 NP 2 2|3 2|3 2|3 - 0F 00 /1 PM= 23|27 STR r|m32 NP 2 2|3 2|3 - - 0F 00 /1 PM= 23|27
Pseudocode:
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
else
; RM oder VM
Int 6
endif
DEST = TR
Beschreibung:
Mit STR lässt sich das TR (Task Register, nicht Test Register!) auslesen und in DEST speichern. Auch das TR ist zwar ein Descriptor Cache ähnlich dem GDTR, aber STR speichert nicht Base und Limit aus dem Cache (wie es SGDT macht), sondern nur einen Selector zu einem TSS-Descriptor im GDT.
STR wird nur im PM erkannt und in der Regel nur von Betriebssystemen be- nutzt.
HINWEIS: Bei Einsatz der 32bit-Variante von STR ist das Hi-Word in DEST nach der Ausführung natürlich undefiniert. Ein Selector ist nun mal nur 16bit groß... ;)
HINWEIS: Einige Dokumentationen besagen, STR würde zu den privilegierten Instructions zählt. Das ist definitiv falsch.
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #UD, #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | SUB - Subtract |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
SUB AL, imm8 UV 1 1 2 3 4 2C ib SUB AX, imm16 UV 1 1 2 3 4 2D iw SUB EAX, imm32 UV 1 1 2 - - 2D id SUB r|m8, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 80 /5 ib SUB r|m16, imm16 UV 1|3 1|3 2|7 3|7 4|17+EA 81 /5 iw SUB r|m32, imm32 UV 1|3 1|3 2|7 - - 81 /5 id SUB r|m16, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 83 /5 ib SUB r|m32, imm8 UV 1|3 1|3 2|7 - - 83 /5 ib SUB r|m8, r8 UV 1|3 1|3 2|7 2|7 3|16+EA 28 /r SUB r|m16, r16 UV 1|3 1|3 2|7 2|7 3|16+EA 29 /r SUB r|m32, r32 UV 1|3 1|3 2|7 - - 29 /r SUB r8, r|m8 UV 1|2 1|2 2|6 2|7 3|9+EA 2A /r SUB r16, r|m16 UV 1|2 1|2 2|6 2|7 3|9+EA 2B /r SUB r32, r|m32 UV 1|2 1|2 2|6 - - 2B /r
Pseudocode:
DEST = DEST - SRC
Beschreibung:
Subtrahiert SRC von DEST und legt das Ergebnis in DEST ab. Beispiel:
mov ax, 8 sub ax, 3 =================== AX = 5
Ist DEST ein Word oder DWord und SRC ein imm8, wird SRC von der CPU vor der Addition automatisch vorzeichenbehaftet auf Word/DWord erweitert.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | TEST - Logical Compare |0 - - - * * ? * 0| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
TEST AL, imm8 UV 1 1 2 3 4 A8 ib TEST AX, imm16 UV 1 1 2 3 4 A9 iw TEST EAX, imm32 UV 1 1 2 - - A9 id TEST r|m8, imm8 NP 1|2 1|2 2|5 3|6 5|11+EA F6 /0 ib TEST r|m16, imm16 NP 1|2 1|2 2|5 3|6 5|11+EA F7 /0 iw TEST r|m32, imm32 NP 1|2 1|2 2|5 - - F7 /0 id TEST r|m8, r8 UV 1|2 1|2 2|5 2|6 3|9+EA 84 /r TEST r|m16, r16 UV 1|2 1|2 2|5 2|6 3|9+EA 85 /r TEST r|m32, r32 UV 1|2 1|2 2|5 - - 85 /r
Pseudocode:
Tmp = DEST AND SRC ZF = Tmp = 0 SF = Tmp[MSB] CF = 0 OF = 0 AF = ?
Beschreibung:
Mit den korrespondierenden Bits von DEST und SRC wird eine logische UND- Verknüpfung durchgeführt. Im Prinzip handelt es sich bei TEST lediglich um eine Variante der AND Instruction. Folglich gilt für TEST die gleiche Wahrheitstabelle, wie für AND:
0 AND 0 = 0 0 AND 1 = 0 1 AND 0 = 0 1 AND 1 = 1
Die Vorteile von TEST gegenüber AND sind die höhere Ausführungsgeschwin- digkeit bei Verwendung von Speicheroperanden, sowie der generelle Aus- schluß einer AGI-Gefahr. Im Gegensatz zu AND wird DEST durch TEST nicht beschrieben. Kein Schreibzugriff, kein Address Generation Interlock.. ;)
TEST wird üblicherweise eingesetzt, um ein Byte, Word oder DWord auf be- stimmte Bitzustände zu prüfen. Beispiel:
mov dx, 03daH ; Input
@@1: in al, dx test al, 8 ; Bit 3 gesetzt? je @@1 ; nein ->
; Vertical Retrace läuft
HINWEIS: Laut offizieller P5-Doku wird durch TEST angeblich ein Schreib- zugriff auf DEST durchgeführt. Zitat:
"DEST = LeftSRC AND RightSRC [...]"
Auch die P6-Doku zu TEST liegt etwas daneben:
"TEMP = SRC1 AND SRC2 [...] IF TEMP = 0 THEN ZF = 0; ELSE ZF = 1; [...]"
Also irgendwie glaub' ich weder an die drei Operanden der P5-, noch an ZF=0 bei Verknüpfung=0 in der P6-Doku... ;)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | VERR - Verify Segment for Reading |- - - - - * - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
VERR r|m16 NP 7 11 10|11 14|16 - 0F 00 /4
Pseudocode:
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
if SRC_SelIdx_out_of_DesTblLimits then ZF = 0 else if SRC_Des.Type <> (Data OR ReadableCode) then ZF = 0 else ZF = 1 if SRC_Des.Type = Non_conforming_CS then if ((CPL > SRC_Des.DPL) OR (RPL > SRC_Des.DPL)) then ZF = 0 endif endif endif endif
else
; RM oder VM
Int 6
endif
Beschreibung:
Mit VERR lässt sich vorab prüfen, ob geplante Lesezugriffe auf ein be- stimmtes Segment überhaupt möglich wären, ohne dabei eine Exception aus- zulösen. Die Auswahl des zu prüfenden Segmentes geschieht dabei indirekt durch eine entsprechende Selector-Angabe in SRC.
Entspricht das zugehörige Segment allen Erfordernissen für einen Lesezu- griff, wird ZF=1, anderenfalls ZF=0 gesetzt.
VERR wird nur im PM erkannt und führt im RM und VM zu Auslösung von In- terrupt 6 (#UD, Invalid Opcode).
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #SS[0], #GP[0], #PF[Code], #AC[0]
PMO O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | VERW - Verify Segment for Writing |- - - - - * - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
VERR r|m16 NP 7 11 15|16 14|16 - 0F 00 /5
Pseudocode:
if ((PE=1) AND (VM=0)) then
; reiner Protected Mode (kein VM)
if SRC_SelIdx_out_of_DesTblLimits then ZF = 0 else if SRC_Des.Type <> WritableDATA then ZF = 0 else ZF = 1 endif endif
else
; RM oder VM
Int 6
endif
Beschreibung:
VERW ist das Gegenstück zu VERR. Mit VERW lässt sich prüfen, ob geplante Schreibzugriffe auf ein bestimmtes Datensegment möglich sind, ohne dabei eine Exception auszulösen. Die Auswahl des zu prüfenden Segmentes wird auch hier durch eine entsprechende Selector-Angabe in SRC getroffen.
Entspricht das gewählte Segment allen Erfordernissen für einen Schreib- zugriff, wird ZF=1, anderenfalls ZF=0 gesetzt.
VERW wird nur im PM erkannt und führt im RM und VM zu Auslösung von In- terrupt 6 (#UD, Invalid Opcode).
Exceptions:
RM Int 6 (Invalid) VM Int 6 (Invalid) PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | WAIT - Wait and handle pending FPU Exceptions (if any) |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
WAIT NP 1 1-3 6 3 4+5n 9B
Pseudocode:
while FPU_busy ; do nothing endwhile
while Pending_unmasked_FPU_Exceptions Handle_next_FPU_Exception endwhile
Beschreibung:
WAIT wird im Normalfall unmittelbar nach ESC-Instructions (FPU-Befehlen) plaziert. Durch ein WAIT wird die CPU gezwungen, solange zu warten, bis der FPU-Befehl ausgeführt, und alle eventuell daraus resultierenden FPU- Exceptions abgewickelt wurden.
Diese Vorgehensweise ist für eine ISR, die unmaskierte FPU-Exceptions behandlen soll, unerläßlich, da CPU und FPU parallel betrieben werden und somit die Möglichkeit besteht, daß die CPU Daten überschreibt, die von der ISR noch für das Exception Handling benötigt werden.
Betroffen sind hiervon im Prinzip alle FN??? Instructions, also die FPU- Befehle, die einen sogenannten Non-waiting State besitzen. Zu den über das FPU Control Word maskierbaren FPU-Exceptions zählen:
#D Denormalized Operand #IA Invalid Arithmetic Operation) #IS Invalid Stack (Over/Underflow) #O Numeric Overflow #P Precision Fault (Inexact Result) #U Numeric Underflow #Z Divide by Zero
Die einzige mögliche CPU-Exception durch ein WAIT ist Interrupt 7 (#NM, no math), falls CR0.MP und CR0.TS gleichzeitig gesetzt sind.
Detaillierte Erläuterungen zu allen FPU-Befehlen (ESC-Opcodes) sind im Topic BAW0011 (FPU-Befehlssatz) enthalten.
Exceptions:
RM Int 7 (No math) VM Int 7 (No math) PM #NM
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | WBINVD - Write Back and Invalidate Cache |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
WBINVD NP 2000+ 5 - - - 0F 09
Pseudocode:
Flush_Internal_Cache Send_Write_Back_Signal_for_external_Cache Send_Flush_Signal_for_external_Cache
Beschreibung:
WBINVD löscht alle internen CPU Cache Lines, und sendet danach zwei spe- zielle Bus Cycles: einen, der dazu führt, daß die die Daten im externen Cache zurück in den Hauptspeicher geschrieben werden, und einen, der den externen Cache danach löscht.
WBINVD ist eine privilegierte Instruction und nur bei CPL0 ausführbar.
Zudem handelt es sich bei WBINVD um eine implementationsspezifische Ins- truction; die Funktion des Befehles kann auf zukünftigen CPUs variieren.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #GP[0] PM #GP[0]
PRI O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | WRMSR - Write Model Specific Register |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
WRMSR NP 30-45 - - - - 0F 30
Pseudocode:
MSR[ECX] = EDX:EAX
Beschreibung:
WRMSR ist das Gegenstück zu RDMSR. Mit WRMSR lässt sich der in EDX:EAX übergebene Wert im ECXten MSR (Model Specific Register) speichern.
HINWEIS: MSRs sind grundsätzlich modellspezifisch, d.h. Funktion und In- halt der MSRs kann von Modell zu Modell und Prozessorgeneration zu Prozessorgeneration voneinander abweichen.
WRMSR gibt es erst ab P5 CPUs, ist aber auch dort implementationsspezi- fisch. Vor dem Einsatz von WRMSR sollte daher besser erst via CPUID ge- prüft werden, ob der Befehl überhaupt unterstützt wird (gesetztes Bit 5 in EDX nach CPUID(1))... ;)
WRMSR ist eine privilegierte Instruction und kann daher nur unter CPL0 ausgeführt werden. Der Aufruf von WRMSR mit einer ungültigen bzw. reser- vierten Registernummer (P5: 3, 10, 15, und größer 19) führt zur Auslö- sung eines General Protection Faults (#GP[0]).
Detaillierte Informationen zu den einzelnen MSRs befinden sich im Topic BAW0007 (CPU-Registersatz).
Exceptions:
RM Int 6 (Invalid), #GP[0] VM Int 6 (Invalid), #GP[0] PM #UD, #GP[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | XADD - Exchange and Add |* - - - * * * * *| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
XADD r|m8, r8 NP 3|4 3|4 - - - 0F C0 /r XADD r|m16, r16 NP 3|4 3|4 - - - 0F C1 /r XADD r|m32, r32 NP 3|4 3|4 - - - 0F C1 /r
Pseudocode:
Tmp = DEST + SRC SRC = DEST DEST = Tmp
Beschreibung:
XADD ermittelt temporär die Summe von DEST und SRC, speichert dann DEST in SRC und abschließend die temporäre Summe in DEST. Beispiel:
mov eax, 10 mov ebx, 20 xadd eax, ebx ============== eax = 30 ebx = 10
HINWEIS: In einigen 486+ Dokumentationen fehlt dieser Befehl, oder wird fälschlicherweise als nicht flagbeeinflussend dargestellt.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | XCHG - Exchange Operands |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
XCHG AX, r16 NP 2 3 3 3 3 90+rw XCHG EAX, r32 NP 2 3 3 - - 90+rd XCHG r16, AX NP 2 3 3 3 3 90+rw XCHG r32, EAX NP 2 3 3 - - 90+rd XCHG r8, r|m8 NP 3 3|5 3|5 3|5 4|17+EA 86 /r XCHG r16, r|m16 NP 3 3|5 3|5 3|5 4|17+EA 87 /r XCHG r32, r|m32 NP 3 3|5 3|5 - - 87 /r XCHG r|m8, r8 NP 3 3|5 3|5 3|5 4|17+EA 86 /r XCHG r|m16, r16 NP 3 3|5 3|5 3|5 4|17+EA 87 /r XCHG r|m32, r32 NP 3 3|5 3|5 - - 87 /r
Pseudocode:
Tmp = DEST SRC = DEST DEST = Tmp
Beschreibung:
XCHG vertauscht den Inhalt von DEST und SRC. Beispiel:
mov eax, 10 mov ebx, 20 xchg eax, ebx ============== eax = 20 ebx = 10
HINWEIS: Bei Einsatz von XCHG mit einem Speicheroperanden sendet die CPU automatisch LOCK# für die Dauer der Ausführung. Ein explizites LOCK Prefix durch den Programmierer ist hier somit überflüssig.
HINWEIS: Der momentane IOPL hat keinen Einfluss auf die automatische Ge- neration des LOCK# Signals.
HINWEIS: In der offiziellen P6-Doku wird behauptet (Zitat):
"[...] When the operands are two registers, one of the registers must be the EAX or AX register. [...]"
Das glauben wir natürlich nicht, oder? ;)
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | XLAT - Table Lookup Translation |- - - - - - - - -| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
XLATB NP 4 4 5 5 11 D7 XLAT m8 NP 4 4 5 5 11 D7
Pseudocode:
AL = [byte ptr ?S:eBX + AL]
Beschreibung:
XLAT lädt das Byte, welches sich bei Offset eBX+AL relativ zum gewählten Segment befindet, nach AL. Wird kein Segment Override angegeben, wird DS als Standardsegment benutzt. Beispiel:
Table db 3, 2, 1, 0
mov bx, Offset Table mov al, 3 xlat ======================= AL = 0
HINWEIS: Der Indexpart (AL) wird bei Ausführung von XLAT leider automa- tisch immer als vorzeichenloser (unsigned) Wert interpretiert. Der Indexbereich erstreckt sich folglich von 0 bis 255 relativ zum Beginn der Tabelle bei ?S:eBX.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
O D I T S Z A P C +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+ | XOR - Logical Exclusive OR |0 - - - * * ? * 0| +--------------------------------------------------------+-+-+-+-+-+-+-+-+-+
Befehl Pipe P5 486 386 286 86 Opcode
XOR AL, imm8 UV 1 1 2 3 4 34 ib XOR AX, imm16 UV 1 1 2 3 4 35 iw XOR EAX, imm32 UV 1 1 2 - - 35 id XOR r|m8, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 80 /6 ib XOR r|m16, imm16 UV 1|3 1|3 2|7 3|7 4|17+EA 81 /6 iw XOR r|m32, imm32 UV 1|3 1|3 2|7 - - 81 /6 id XOR r|m16, imm8 UV 1|3 1|3 2|7 3|7 4|17+EA 83 /6 ib XOR r|m32, imm8 UV 1|3 1|3 2|7 - - 83 /6 ib XOR r|m8, r8 UV 1|3 1|3 2|7 2|7 3|16+EA 30 /r XOR r|m16, r16 UV 1|3 1|3 2|7 2|7 3|16+EA 31 /r XOR r|m32, r32 UV 1|3 1|3 2|7 - - 31 /r XOR r8, r|m8 UV 1|2 1|2 2|6 2|7 3|9+EA 32 /r XOR r16, r|m16 UV 1|2 1|2 2|6 2|7 3|9+EA 33 /r XOR r32, r|m32 UV 1|2 1|2 2|6 - - 33 /r
Pseudocode:
DEST = DEST XOR SRC CF = 0 OF = 0
Beschreibung:
Mit den korrespondierenden Bits von DEST und SRC wird eine logische, ex- klusive ODER-Verknüpfung durchgeführt. Das Ergebnis der Verknüpfung wird in DEST abgelegt. Wahrheitstabelle XOR:
0 XOR 0 = 0 0 XOR 1 = 1 1 XOR 0 = 1 1 XOR 1 = 0
XOR wird u.a. häufig in Einfachverschlüsselungen eingesetzt, da ein dop- peltes XOR eines Wertes mit ein- und demselben Schlüsselwert wieder den ursprünglichen Wert herstellt. Beispiel:
mov al, 01000001B ; = 41H = 'A' xor al, 10101010B ==================== al = 11101011B ; = ebH = 'ë'
mov al, 11101011B ; = ebH = 'ë' xor al, 10101010B ==================== al = 01000001B ; = 41H = 'A'
Ist DEST ein Word oder DWord und SRC ein imm8, wird SRC von der CPU vor der Addition automatisch vorzeichenbehaftet auf Word/DWord erweitert.
Exceptions:
RM Int 13 (Wrap) VM Int 13 (Wrap), #PF[Code], #AC[0] PM #SS[0], #GP[0], #PF[Code], #AC[0]
MMX-Befehlssatz
Thomas-Ivo Heinen 2:2449/533:
[Hinweis: Die Informationen über MMX sind (seitens Intel) nur spärlich und IMHO auch widersprüchlich - das eine Mal existieren bestimmte Addressie- rungsarten, das andere Mal nicht. Darum bitte ich um Verständnis für Fehler]
Ab dem PentiumMMX hat INTEL 57 neue Befehle und 8 neue 64-Bit Register ein- geführt - unter der Bezeichnung MMX (Multimedia Extensions). Damit können nun bis zu 8 (!) arithmetische Operationen gleichzeitig durchgeführt werden. Bisher sollen folgende Prozessoren MMX unterstützen:
- iPentium MMX - iPentium II (und alle folgenden Generationen) - AMD K6 - Cyrix 6x86MX - IDT C6
Etliche Applets, Spezifikationen, Beispiele und Dokumentationen über CPUs etc. von AMD, CYRIX und INTEL können bei 2:2449/533 requestet werden. Wer an ihnen interessiert ist, sollte die Fileliste requesten - eine Auflistung wäre zu umfangreich- oder interessiert sich hier jemand für Multiprocessing?
Der einzige mir bekannte Assembler, der auch MMX unterstützt, ist NASM. Er ist auf www.simtel.com zu finden oder kann mit "NASM" bei 2:2449/533 re- questet werden. Vergesst TASM ! ;)
MMX bringt das Konzept SIMD (Single Instruction Multiple Data) mit sich, das bis zu acht Operationen mit einem Befehl ermöglicht. Es wird auch unter- schieden zwischen Wrap-Around (Bei Overflow beginnt das Register wieder bei 0) und Saturation (Bei Signed stoppt bei Byte es bei -127 bzw. 128, bei Unsigned bei 0 bzw. 255).
Die Erkennung von MMX geschieht über das 23. Bit des Featureflags der CPUID- Instruktion (IA_MMX-Bit).
32-BIT REGISTERBEZEICHNER (rrr)
000 EAX | 100 ESP 001 EBX | 101 EBP 010 ECX | 110 ESI 011 EDX | 111 EDI
MMX-REGISTERNUMMERN (mmm, nnn)
Diese werden wie folgt kodiert (Werte in die mmm/nnn-Felder einsetzen):
000 MMX-Register0 - mm0 ... ... 111 MMX-Register7 - mm7
Im Folgenden werden die Befehle kurz erläutert.
Da keiner dieser Befehle die Flags beeinflußt, sowie die Befehle in allen Modi ein gleiches Verhalten zeigen, ändert sich der Header dementsprechend:
+--------------------------------------------------------------------------+ | Mnemonic und Befehlsname | +--------------------------------------------------------------------------+
Nach dem Kasten folgt eine Tabelle mit folgendem Format:
Befehl PIPE P-II P-MMX Opcode
PADDD mmx16,mmx16 UV 1 1 0F FD 11mmmnnn
BEFEHL
In dieser Spalte werden alle möglichen Adressierungsarten des jeweiligen Befehles aufgeführt. Dabei werden folgende Abkürzungen verwendet:
reg32 32bit-Register (386+) EAX, EBX, ECX, EDX, ESI, EDI, EBP oder ESP mem16 16bit-Speicheroperand mem32 32bit-Speicheroperand mem64 64bit-Speicheroperand imm8 8bit-Konstante mmx8 MMX-Register mit Byte-Granularity mmx16 MMX-Register mit Word-Granularity mmx32 MMX-Register mit DWord-Granularity mmx64 MMX-Register mit QWord-Granularity
PIPE
... hat viel Spass gemacht, das rauszubekommen :)
Das Pairing gilt selbstverständlich nur fuer Intel-MMX-CPUs, da diese als einzige zwei MMX-Pipes haben. AMD, IDT und CYRIX haben nur eine Pipe, da kommt allerhöchstens das MMX-Integer-Pairing in Frage, was angeblich in der Lage ist, eine MMX- mit zwei Integer-Instructions zu pairen. Da ist Intel mit der Möglichkeit, nur einen MMX- mit einem Integer-Befehl zu pairen etwas zurück ;)
a) MMX-MMX Pairing
Das Pairing der MMX-Instructions ist relativ einfach, wenn man sich ein paar Grundregeln anlernt. Natürlich sollten auch Dependencies der Re- gister vermieden werden ;)
(Das MMX-Pairing kann übrigens per CR0.TS abgeschaltet werden)
a1. Es können keine zwei Befehle der Multiply-Unit zusammen gepaired werden (PMULL, PMULH, PMADD).
a2. Es können keine zwei Befehle der Shifting-Unit zusammen gepaired werden (PACK, UNPCK, PSLA, PSRA, PSRL).
a3. Speicher-/Integer-Registerzugriffe können nur in der U-Pipe bear- beitet werden.
Daher ergeben sich folgende Kürzel für reines MMX-MMX Pairing:
NP nicht pairable PU pairable, wenn der Befehl in der U-Pipe ist UV pairable, egal ob in U- oder V-Pipe pu pairable, wenn der Befehl in der U-Pipe ist, aber nicht zwei Be- fehle der selben Unit (-> a1. & a2.) uv pairable, egal ob in U- oder V-Pipe, aber nicht zwei Befehle der selben Unit (-> a1. & a2.)
b) MMX-Integer Pairing
Das geht ;)
MMX- und Integer-Befehle können gepaired werden, allerdings immer nur ein MMX- mit einem Integer-Befehl. Dabei ist folgendes zu beachten:
b1. Die MMX-Instruction darf nicht auf Speicher oder Integerregister zugreifen, sonst ist ein MMX-Integer Pairing unmöglich.
b2. Die Integer-Instruction muss in der Pipe, in der sie landet, pair- able sein.
c) FPU-MMX Pairing
Geht natürlich nicht, solange MMX und FPU dieselben CPU-Bereiche ver- wenden. Da müssen wir hoffen, dass Floatingpoint-Stack und MMX-Register bald voneinander "entkoppelt" werden.
Noch einige generelle Anmerkungen:
- Die Multiplikationsbefehle der MMX-Unit benötigen drei Taktzyklen zur Ausführung. Derweil arbeitet die andere Pipeline aber weiter (!). Soll heissen, nach einer Multiplikation sollten die folgenden drei Befehle keine Dependancies mit dem Register der Multiplikation erzeugen.
- Das Abspeichern eines MMX-Registers in Speicher/Integerregister sollte man sehr sorgfältig behandeln. Soll heissen, man muss dem MMX-Register zwei Taktzyklen Ruhe gönnen, bevor man es beschreibt, sonst kassiert man einen Pipeline-Stall (igitt)
- Möglichst Speicherzugriffe vermeiden. Das hat auf Pentium MMX keinen grossartigen Effekt (ausser PU *g*), aber der Pentium-II und der bald wohl erscheinende Pentium Pro-MMX haben bei Speicherzugriffen einen MicroOp mehr zu tun und daher kann's langsamer werden...
Na, alle Klarheiten beseitigt ? ;)
P-MMX, P-II
In diesen Spalten werden die Anzahl Taktzyklen angegeben, die ein Befehl auf dem jeweiligen Prozessor zur Ausführung benötigt. Die Abkürzungen ste- hen für:
- P-MMX: Pentium MMX "Normaler" Pentium mit grösserem Cache und zwei MMX-Pipelines
- P-II : Pentium II Pentium Pro in neuem Gewand mit etlichen inter- nen Änderungen ... und zwei MMX-Pipelines.
Die zeitliche Länge der Befehle liegt bei MMX bis auf die PM...-Befehle bei den Intel-Prozessoren bei nur einem Tick, die entsprechenden Spalten werden somit fast zur Farce.
OPCODE
ib Opcode, Mod R/M- oder SIB-Byte folgt eine 8bit-Konstante /Ziffer Der Befehl verwendet nur einen R/M Operanden. Die Ziffer (0..7) im RG/OP-Feld des Mod R/M Bytes bestimmt, welche Art Erweiterung der Opcode bekommt. r/m Der Befehl verwendet einen R/M und einen Register-Operanden. mmm Nummer des ersten MMX-Registers nnn Nummer des ersten MMX-Registers rrr Registerbezeichner (32bit) s.u. oo Modus
BESCHREIBUNG
Ausführliche Beschreibung des Befehls und eventueller Besonderheiten.
M&Oml;GLICHE EXCEPTIONS
Angaben zu möglichen Exceptions bei der Anwendung des jeweiligen Befehles.
+--------------------------------------------------------------------------+ | EMMS - Empty MMX State | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
EMMS NP 1 1 0F 77
Beschreibung:
Löscht das Statusbyte der MMX. Nur erforderlich am Ende einer MMX-Unter- routine, aber nur wirklich notwendig, wenn im gleichen Programm auch Fließkomma-Arithmetik verwendet wird.
Exceptions:
#UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | MOVD - Move Doubleword | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
MOVD mmx32,reg32 PU 1 1 0F 6E 11mmmrrr MOVD reg32,mmx32 PU 1 1 0F 7E 11mmmrrr MOVD mmx32,mem32 PU 1 1 0F 6E oommmr/m MOVD mem32,mmx32 PU 1 1 0F 7E oommmr/m
Beschreibung:
Kopiert die Inhalte von Speicher/Registern und MMX. Arbeitet genau wie MOV - allerdings mit 32 Bit...
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | MOVQ - Move Quadword | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
MOVQ mmx64,mmx64 UV 1 1 0F 6F 11mmmnnn ? MOVQ mmx64,mmx64 UV 1 1 0F 7F 11mmmnnn MOVQ mmx64,mem64 PU 1 1 0F 6F oommmr/m MOVQ mem64,mmx64 PU 1 1 0F 6F oommmr/m
Beschreibung:
Kopiert die Inhalte von Speicher und MMX. Arbeitet genau wie MOVD - aber mit 64 Bit...
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PACKSSDW - Pack DWord to Word Data (Signed with Saturation) | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PACKSSDW mmx64,mmx64 uv 1 1 0F 6B 11mmmnnn PACKSSDW mmx64,mem64 pu 1 1 0F 6B oommmr/m
Beschreibung:
Konvertiert die je zwei signed DWords von SRC und DEST in vier signed Words in DEST (!?)
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PACKSSWB - Pack Word to Byte Data (Signed with Saturation) | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PACKSSWB mmx64,mmx64 uv 1 1 0F 63 11mmmnnn PACKSSWB mmx64,mem64 pu 1 1 0F 63 oommmr/m
Beschreibung:
Konvertiert die je vier signed Words von SRC und DEST in acht signed Words in DEST (!?)
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PACKUSWB - Pack Word to Byte Data (Unsigned with Saturation) | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PACKUSWB mmx8,mmx16 uv 1 1 0F 67 11mmmnnn PACKUSWB mmx8,mem16 pu 1 1 0F 67 oommmr/m
Beschreibung:
Konvertiert die je vier signed Words von SRC und DEST in acht unsigned Words in DEST (!?)
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PADD - Add with Wrap-Around | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PADDB mmx8,mmx8 UV 1 1 0F FC 11mmmnnn PADDB mmx8,mem8 PU 1 1 0F FC oommmr/m PADDD mmx16,mmx16 UV 1 1 0F FD 11mmmnnn PADDD mmx16,mem16 PU 1 1 0F FD oommmr/m PADDW mmx32,mmx32 UV 1 1 0F FE 11mmmnnn PADDW mmx32,mem32 PU 1 1 0F FE oommmr/m
Beschreibung:
Addiert den zweiten Parameter zum ersten hinzu. Wenn daraus ein Overflow entsteht, "dreht" sich das Register wieder auf 0.
Beispiel:
mov eax, 10204080H mov ebx, 80808090H movd mm0, eax movd mm1, ebx paddb mm0, mm1
=> mm0 = 90a0c010H
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PADDS - Add Signed with Saturation | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PADDSB mmx8,mmx8 UV 1 1 0F EC 11mmmnnn PADDSB mmx8,mem8 PU 1 1 0F EC oommmr/m PADDSW mmx16,mmx16 UV 1 1 0F ED 11mmmnnn PADDSW mmx16,mem16 PU 1 1 0F ED oommmr/m
Beschreibung:
Addiert den zweiten Parameter zum ersten hinzu. Wenn daraus ein Overflow entsteht, bleibt das Register auf dem entsprechenden Wert stehen.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PADDUS - Add Unsigned with Saturation | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PADDUSB mmx8,mmx8 UV 1 1 0F DC 11mmmnnn PADDUSB mmx8,mem8 PU 1 1 0F DC oommmr/m PADDUSW mmx16,mmx16 UV 1 1 0F DD 11mmmnnn PADDUSW mmx16,mem16 PU 1 1 0F DD oommmr/m
Beschreibung:
Addiert den zweiten Parameter zum ersten hinzu. Wenn daraus ein Overflow entsteht, bleibt das Register auf dem entsprechenden Wert stehen.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PAND - Bitwise And | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PAND mmx64,mmx64 UV 1 1 0F DB 11mmmnnn PAND mmx64,mem64 PU 1 1 0F DB oommmr/m
Beschreibung:
Bitweises AND eines 64bit-Registers.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PANDn - Bitwise AndNot | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PANDN mmx64,mmx64 UV 1 1 0F DF 11mmmnnn PANDN mmx64,mem64 PU 1 1 0F DF oommmr/m
Beschreibung:
Bitweises NAND eines 64bit-Registers.
Wahrheitstabelle NAND:
0 NAND 0 = 1 0 NAND 1 = 1 1 NAND 0 = 1 1 NAND 1 = 0
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PCMPEQ - Packed Compare for Equality | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PCMPEQB mmx8,mmx8 UV 1 1 0F 74 11mmmnnn PCMPEQB mmx8,mem8 PU 1 1 0F 74 oommmr/m PCMPEQW mmx16,mmx16 UV 1 1 0F 75 11mmmnnn PCMPEQW mmx16,mem16 PU 1 1 0F 75 oommmr/m PCMPEQD mmx32,mmx16 UV 1 1 0F 76 11mmmnnn PCMPEQD mmx32,mem16 PU 1 1 0F 76 oommmr/m
Beschreibung:
Liefert im ersten Operanden ffH/ffffH bzw. ffffffffH falls die Bedingung erfüllt ist, sonst 00H/0000H/00000000H.
Beispiel:
mov eax, 12342345H mov ebx, 12341234H movd mm0, eax movd mm1, ebx pcmpeq mm0, mm1
=> mm0 = ffff0000H
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PCMPGT - Packed Compare Greater (Signed) | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PCMPGTB mmx8,mmx8 UV 1 1 0F 64 11mmmnnn PCMPGTB mmx8,mem8 PU 1 1 0F 64 oommmr/m PCMPGTW mmx16,mmx16 UV 1 1 0F 65 11mmmnnn PCMPGTW mmx16,mem16 PU 1 1 0F 65 oommmr/m PCMPGTD mmx32,mmx16 UV 1 1 0F 66 11mmmnnn PCMPGTD mmx32,mem16 PU 1 1 0F 66 oommmr/m
Beschreibung:
Liefert im ersten Operanden ffH/ffffH bzw. ffffffffH falls die Bedingung erfüllt ist, sonst 00H/0000H/00000000H.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PMADD - Packed Multiply Add | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PMADD mmx64,mmx64 uv 3 3 0F F5 11mmmnnn PMADD mmx64,mem64 pu 3 3 0F F5 oommmr/m
Beschreibung:
dest(31..0) <--dest(15..0) * src(15..0) + dest(31..16) * src(31..16); dest(63..32)<--dest(47..32) * src(47..32) + dest(63..48) * src(63..48);
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PMULH - Packed Multiplication (HighWord) | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PMULH mmx64,mmx64 uv 3 3 0F E5 11mmmnnn PMULH mmx64,mem64 pu 3 3 0F E5 oommmr/m
Beschreibung:
Die vier signed Words von DEST werden mit denen von SRC multipliziert. Die oberen 16Bit der jeweiligen Ergebnisse werden nach DEST geschrieben.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PMULL - Packed Multiplication (LowWord) | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PMULL mmx64,mmx64 uv 3 3 0F D5 11mmmnnn PMULL mmx64,mem64 pu 3 3 0F D5 oommmr/m
Beschreibung:
Die vier signed Words von DEST werden mit denen von SRC multipliziert. Die oberen 16Bit der jeweiligen Ergebnisse werden nach DEST geschrieben.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | POR - Bitwise Or | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
POR mmx64,mmx64 UV 1 1 0F EB 11mmmnnn POR mmx64,mem64 PU 1 1 0F EB oommmr/m
Beschreibung:
Bitweises OR eines 64bit-Registers.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PSLL - Packed Shift Left Logical | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PSLLW mmx16,mmx16 uv 1 1 0F F1 11mmmnnn PSLLW mmx16,mem16 pu 1 1 0F F1 11mmmr/m PSLLW mmx16,imm8 uv 1 1 0F F1 11110mmm ib PSLLD mmx32,mmx32 uv 1 1 0F F2 11mmmnnn PSLLD mmx32,mem32 pu 1 1 0F F2 11mmmr/m PSLLD mmx32,imm8 uv 1 1 0F F2 11110mmm ib PSLLQ mmx64,mmx64 uv 1 1 0F F3 11mmmnnn PSLLQ mmx64,mem64 pu 1 1 0F F3 11mmmr/m PSLLQ mmx64,imm8 uv 1 1 0F F3 11110mmm ib
Beschreibung:
Äquivalent zu SHL mit jeweiliger Bitbreite. Also werden z.B. die Werte des Words um xxx geshifted, agieren aber autark, d.h. das MMX-Register wird NICHT als ein 64-Bit-Register betrachtet, sondern als z.B. vier ei- genständige Words, die alle um jeweils xx geshiftet werden.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PSRA - Packed Shift Right Arithmetic | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PSRAW mmx16,mmx16 uv 1 1 0F E1 11mmmnnn PSRAW mmx16,mem16 pu 1 1 0F E1 11mmmr/m PSRAW mmx16,imm8 uv 1 1 0F E1 11100mmm ib PSRAD mmx32,mmx32 uv 1 1 0F E2 11mmmnnn PSRAD mmx32,mem32 pu 1 1 0F E2 11mmmr/m PSRAD mmx32,imm8 uv 1 1 0F E2 11100mmm ib
Beschreibung:
Äquivalent zu SAR mit jeweiliger Bitbreite. Also werden z.B. die Werte des Words um xxx geshifted, agieren aber autark, d.h. das MMX-Register wird NICHT als ein 64-Bit-Register betrachtet, sondern als z.B. vier ei- genständige Words, die alle um jeweils xx geshiftet werden.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PSRL - Packed Shift Right Logical | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PSRLW mmx16,mmx16 uv 1 1 0F D1 11mmmnnn PSRLW mmx16,mem16 pu 1 1 0F D1 11mmmr/m PSRLW mmx16,imm8 uv 1 1 0F D1 11010mmm ib PSRLD mmx32,mmx32 uv 1 1 0F D2 11mmmnnn PSRLD mmx32,mem32 pu 1 1 0F D2 11mmmr/m PSRLD mmx32,imm8 uv 1 1 0F D2 11010mmm ib PSRLQ mmx64,mmx64 uv 1 1 0F D3 11mmmnnn PSRLQ mmx64,mem64 pu 1 1 0F D3 11mmmr/m PSRLQ mmx64,imm8 uv 1 1 0F D3 11010mmm ib
Beschreibung:
Äquivalent zu SHR mit jeweiliger Bitbreite. Also werden z.B. die Werte des Words um xxx geshifted, agieren aber autark, d.h. das MMX-Register wird NICHT als ein 64-Bit-Register betrachtet, sondern als z.B. vier ei- genständige Words, die alle um jeweils xx geshiftet werden.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PSUB - Subtract with Wrap-Around | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PSUBB mmx8,mmx8 UV 1 1 0F FB 11mmmnnn PSUBB mmx8,mem8 PU 1 1 0F FB oommmr/m PSUBW mmx16,mmx16 UV 1 1 0F FC 11mmmnnn PSUBW mmx16,mem16 PU 1 1 0F FC oommmr/m PSUBD mmx32,mmx32 UV 1 1 0F FD 11mmmnnn PSUBD mmx32,mem32 PU 1 1 0F FD oommmr/m
Beschreibung:
Subtrahiert den zweiten Parameter vom ersten. Wenn daraus ein Underflow entsteht, "dreht" sich das Register wieder auf ffh.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PSUBS - Subtract Signed with Saturation | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PSUBSB mmx8,mmx8 UV 1 1 0F E8 11mmmnnn PSUBSB mmx8,mem8 PU 1 1 0F E8 oommmr/m PSUBSW mmx16,mmx16 UV 1 1 0F E9 11mmmnnn PSUBSW mmx16,mem16 PU 1 1 0F E9 oommmr/m
Beschreibung:
Subtrahiert den zweiten Parameter vom ersten. Wenn daraus ein Underflow entsteht, "dreht" sich das Register wieder auf ffh.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PSUBUS - Subtract Unsigned with Saturation | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PSUBUSB mmx8,mmx8 UV 1 1 0F D8 11mmmnnn PSUBUSB mmx8,mem8 PU 1 1 0F D8 oommmr/m PSUBUSW mmx16,mmx16 UV 1 1 0F D9 11mmmnnn PSUBUSW mmx16,mem16 PU 1 1 0F D9 oommmr/m
Beschreibung:
Subtrahiert den zweiten Parameter vom ersten. Wenn daraus ein Underflow entsteht, "dreht" sich das Register wieder auf ffh.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PUNPCKH - Unpack High Data to Next Larger | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PUNPCKHB mmx8,mmx8 uv 1 1 0F 68 11mmmnnn PUNPCKHB mmx8,mem8 pu 1 1 0F 68 oommmr/m PUNPCKHW mmx16,mmx16 uv 1 1 0F 69 11mmmnnn PUNPCKHW mmx16,mem16 pu 1 1 0F 69 oommmr/m PUNPCKHD mmx32,mmx32 uv 1 1 0F 6A 11mmmnnn PUNPCKHD mmx32,mem32 pu 1 1 0F 6A oommmr/m
Beschreibung:
Originalbeschreibung: "Interleaves the bits of the operands", hier für die oberen Bits. Ein Beispiel tut's am Besten:
--- PKUNPCKHB --- --- PKUNPCKHW ---
dest(63..56)<--src (63..56); desc(63..48)<--src (63..48) dest(55..48)<--dest(63..56); desc(47..32)<--dest(63..48) dest(47..40)<--src (55..48); desc(31..16)<--src (47..32) dest(39..32)<--dest(55..48); desc(15..0) <--dest(57..32) dest(31..24)<--src (47..40); dest(23..16)<--dest(47..40); dest(15..8) <--src (39..32); dest(7..0) <--dest(39..32);
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PUNPCKL - Unpack Low Data to Next Larger | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PUNPCKLB mmx8,mmx8 uv 1 1 0F 60 11mmmnnn PUNPCKLB mmx8,mem8 pu 1 1 0F 60 oommmr/m PUNPCKLW mmx16,mmx16 uv 1 1 0F 61 11mmmnnn PUNPCKLW mmx16,mem16 pu 1 1 0F 61 oommmr/m PUNPCKLD mmx32,mmx32 uv 1 1 0F 62 11mmmnnn PUNPCKLD mmx32,mem32 pu 1 1 0F 62 oommmr/m
Beschreibung:
Originalbeschreibung: "Interleaves the bits of the operands", hier für die unteren Bits. Ein Beispiel tut's am Besten:
--- PKUNPCKLB --- --- PKUNPCKLW ---
dest(63..56)<--src (31..24); desc(63..48)<--src (31..16) dest(55..48)<--dest(31..24); desc(47..32)<--dest(31..16) dest(47..40)<--src (23..16); desc(31..16)<--src (15..0) dest(39..32)<--dest(23..16); desc(15..0) <--dest(15..0) dest(31..24)<--src (15..8); dest(23..16)<--dest(15..8); dest(15..8) <--src (7..0); dest(7..0) <--dest(7..0);
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
+--------------------------------------------------------------------------+ | PXOR - Bitwise Xor | +--------------------------------------------------------------------------+
Befehl PIPE P-II P-MMX Opcode
PXOR mmx64,mmx64 UV 1 1 0F EF 11mmmnnn PXOR mmx64,mem64 PU 1 1 0F EF oommmr/m
Beschreibung:
Bitweises XOR eines 64bit-Registers.
Exceptions:
#SS, #GP, #PF, #AC bei Mem-Operand #UD, falls kein MMX vorhanden #NM, falls CR0.TS=1
Genauere Informationen ----------------------
Internet: http://www.x86.org http://developer.intel.com http://www.amd.com BBS/Fido: 2:2449/533
FPU-Befehlssatz
Horst Kraemer 2:2410/249.5: Topic in Arbeit
Real Mode
Juergen Thelen 2:2450/55.5:
Beim Real Mode (RM), genauer Real Address Mode, handelt es sich um einen ge- nau definierten Prozessormodus, in welchem alle 80x86 CPUs (und Kompatiblen) betrieben werden können.
Der Modus wurde mit der ersten CPU der PC-Ära - in unserem Raum war das m.W. der 8086 der Firma Intel - eingeführt und existiert aus Gründen der Abwärts- kompatibilität auf allen Nachfolge-CPUs bis hin zum heute aktuellen PPro.
Neben diversen anderen, hier aber eher nebensächlichen Dingen, wird durch den aktiven Prozessormodus auch bestimmt, wie die CPU Angaben von Speicher- adressen interpretiert bzw. in welchem Format diese vorliegen müssen, damit die CPU aus der Angabe eine tatsächliche physikalische Adresse bilden kann.
Im RM erwartet die CPU Adressangaben im Format des segmentierten Speicher- modells. Dieses segmentierte Modell, welches auch heute noch so manchen Ein- steiger zum Wahnsinn treibt ;), entstand aufgrund der Tatsache, daß ein 8086 zwar bereits über einen 20bit breiten Adressbus, leider aber über einen nur 16bit breiten Datenbus verfügte.
Es mußte also ein Format her, welches ermöglicht, den gesamten 20bit breiten Adressraum anzusprechen, gleichzeitig aber auch für einen 16bit Datenbus noch transportierbar ist (und das möglichst schnell).
Naturgemäß wollten die Entwickler die 16bit-Fähigkeiten ihrer Maschine dabei konsequent ausnutzen. Da man mit 16bit maximal 65.536 Bytes, also 64 KByte, ansprechen kann, lag es nahe, die Programmierer jeweils max. 64 KByte große Teile (Segmente) des Speichers nutzen zu lassen:
+----------------+ --+ - 1.024 KByte +----------------+ | | 64 KByte Seg | | +----------------+ +-- Adressraum 20bit (1 MByte = 1024 KByte) : : : : : : | | | | | | +----------------+ --+ - 0 KByte
Soweit, so gut. Doch wie sollte die Position eines 64 KByte-Segmentes inner- halb des 20bit-Adressraumes (1 MByte) festgelegt werden? Daß man 20bit nicht in einem Rutsch übertragen konnte, ist klar (Beispielwert 10000H):
+-- ? --+--------- Word (16bit) --------+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|1|1|1| | | | | | | | | | | | | | | | | |3|2|1|0|f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 = 10000H (20bit-Notation)
Was also machen mit den oberen 4 Bit?
Instinktiv werden jetzt wohl einige sagen: "Kein Problem. Schicken wir halt die obersten 4 Bit in einem weiteren Word und lassen die Adresslogik einfach die anderen 12 Bit dieses zweiten Words ignorieren."
Ok, das hätte auch funktioniert. Eine solche Vorgehensweise birgt aber auch einen gewaltigen Nachteil, der auf den ersten Blick nicht direkt ins Auge fällt. Wären die Entwickler des 8086 tatsächlich so vorgegangen, hätte das zur Folge gehabt, daß die gesamten 1 MByte ansprechbaren Adressraums automa- tisch in 16 feste Segmente unterteilt worden wären:
+----------------+ --+ - 1.024 KByte 16. Seg | f0000..fffffH | | +----------------+ | : : : : : : +----------------+ +-- Adressraum 20bit (1 MByte = 1024 KByte) | 10000..1ffffH | | +----------------+ | 1. Seg | 00000..0ffffH | | +----------------+ --+ - 0 KByte
Das hätte nicht nur die Bewegungsfreiheit des Programmierers eingeschränkt, sondern wäre auch weder ökonomisch gewesen (kleinere Programme hätten immer volle 64 KByte belegt, selbst wenn sie z.B. nur 10 KByte gebraucht hätten), noch hätte man in diesem Fall Segmente sich überlappen lassen können.
Irgendwie scheint die 16 es den Entwicklern damals angetan zu haben ;), je- denfalls entschieden sie, daß es ein guter Kompromiss sei, wenn Segmente auf jeder beliebigen 0MOD16-Boundary (Speicheradresse / 16 = x, Rest 0) begin- nen können. Btw: Häufig liest man statt 0MOD16 auch Paragraph, gemeint ist aber das gleiche. Nachfolgend ein Beispiel für 64 KByte-Segmente, die an ei- ner willkürlich gewählten 0MOD16-Boundary beginnen:
+----------------+ --+ - 1.024 KByte +----------------+ | Seg B | 82a00..919ffH | | +----------------+ | | | | : : : : : : +----------------+ +-- Adressraum 20bit (1 MByte = 1024 KByte) Seg A | 0f7e0..1e6dfH | | +----------------+<------ 0MOD16-Boundary (0f7e0H) | | | +----------------+ --+ - 0 KByte
Da das Senden eines zweiten Words sowieso unumgänglich blieb, um eine 20bit- Adresse zu übertragen, wurde die Adresslogik kurzerhand so programmiert, daß dieses zweite Word (die Segmentadresse) als Vielfaches von 16 interpretiert und dann intern zum zuvor gesendeten (ersten) Word (der Offsetadresse) hin- zuaddiert wird.
Die Schreibweise für eine segmentierte Adresse sieht so aus: 0000:0000H, wo- bei der Hexadezimalwert links vom Doppelpunkt die Segmentadresse, sowie der Hexadezimalwert rechts vom Doppelpunkt die Offsetadresse innerhalb dieses Segmentes darstellt. Zusammenfassend der gesamte Adressbildungsweg noch ein- mal anhand eines Beispiels:
segmentierte Notation 1234:5678H [logische Adresse]
Offset an Adresslogik 5678H
Segment an Adresslogik 1234H (intern * 16 nehmen) 12340H
Offset 5678H Segment + 12340H -------------------------------------- Resultat (20bit-Notation) 179b8H [physikalische Adresse] ======================================
Wer nun auch nachvollziehen kann, warum die segmentierte Angabe 1000:79b8H die gleiche physikalische Adresse beschreibt, wie die Angabe 1234:5678H, der dürfte das segmentierte Speichermodell begriffen haben... B)
Kommen wir abschließend zur einer Besonderheit, die durch den Einsatz des segmentierten Speichermodells überhaupt erst entsteht: Welches ist wohl die größtmögliche segmentierte Angabe?
Richtig: ffff:ffffH...
Das entspricht doch wohl auch der physikalisch größtmöglichen Adresse in ei- nem 20bit-Adressraum, nämlich fffffH, oder?
Mitnichten. Übrigens auch nicht Mitneffen... &):
segmentierte Notation ffff:ffffH [logische Adresse]
Offset an Adresslogik ffffH
Segment an Adresslogik ffffH (intern * 16 nehmen) ffff0H
Offset ffffH Segment + ffff0H -------------------------------------- Resultat (20bit-Notation) 10ffefH [physikalische Adresse] ======================================
Damit liegen wir also bereits im 21bit-Bereich, oder anders gesagt, 65.520 Bytes (ffefH = 65.519 + 1) über der Grenze des 20bit-Adressraums...
Ist also in Wirklichkeit ffff:000fH bzw. f000:ffffH (je nach Belieben) die höchstmögliche segmentierte Angabe? Was macht die CPU denn dann bei Angaben, die 20bit überschreiten?
Beim 8086 ist die Antwort klar und eindeutig: Der 8086 hat nur 20 Adresslei- tungen, ihm bleibt quasi gar nichts anderes übrig, als die Adressen so umzu- brechen, daß sie wieder am Anfang des Speichers erscheinen (dieses Verhalten nennt sich übrigens WrapAround).
Aber was ist mit den Nachfolgern? Der 80286 besitzt 24 Adressleitungen, CPUs ab 80386 gar 32 davon. Hier könnten 21bit-Adressen doch problemlos verarbei- tet werden, oder?
Würden Sie auch ;), wenn es da nicht einen mit dem 80286 eingeführten Me- chanismus gäbe, der sich A20-Gate nennt. Bei diesem Mechanismus handelt es um ein simples Freischaltungsverfahren das die 21ste (A0..A20) und somit al- le darüberliegenden Adressleitungen beeinflusst. Bei der Initialisierung des RM wird A20 einfach gesperrt, woraufhin sich auch alle größeren CPUs genauso verhalten, wie der 8086, halt so, als hätten sie nur 20 Adressleitungen.
Das Freischalten von A20 ist übrigens einer der Schlüssel, die Pseudo-Pro- zessormodi wie RM/Flat4G (s. BAW0015) überhaupt erst ermöglichen... B)
Als weitaus bekannteres Beispiel für die Freischaltung von A20 ist aller- dings eher HIMEM.SYS zu nennen. Bei HIMEM.SYS handelt es sich um einen bei Erwerb von MS-DOS mitgelieferten XMS-Treiber. HIMEM.SYS schaltet A20 frei, um u.a. die besagten 65.520 Bytes oberhalb des 20bit-Adressraums ebenfalls für Programme nutzbar zu machen. Unter DOS wird dieser zusätzliche Speicher- raum dann High Memory Area (HMA) genannt. DOS verwendet die HMA z.B. um auf Anweisung einen Großteil seines Kernels dorthin zu verlagern, was natürlich positiv auf dem Konto freien konventionellen RAMs (unter 640 KByte) zu Buche schlägt.
Protected Mode
Jascha Wetzel 2:246/2024.89: Topic in Arbeit
Virtual 86 (V86) Mode
Topic noch zu vergeben.
RM/Flat4G Mode
Juergen Thelen 2:2450/55.5:
Vorab einige Informationen für solche, die mit dem Begriff RM/Flat4G (andere Programmierer nennen den Modus auch Big, Flat, Huge Real Mode, oder ähnlich) absolut nichts anfangen können:
Es handelt sich hier um die Bezeichnung für einen Pseudo-Betriebsmodus, der es dem Programmierer ermöglicht, auch im Real Address Mode (RM) unter DOS das RAM linear adressieren zu können (normalerweise ist dies aufgrund des im RM üblichen segmentierten Speichermodells (64 KByte) nicht möglich).
Häh? Und was soll das bringen? ;)
Ganz einfach: Speeeeeeeeeeeeed... 8)
Der meist erhebliche Geschwindigkeitszuwachs, der Pseudomodi wie RM/Flat4G so beliebt macht, resultiert aus der Vereinigung gewichtiger Vorteile, die Real und Protected Mode (PM) jeweils besitzen.
Der Hauptvorteil des RM ist dessen Grundschnelligkeit. Diese Grundschnellig- keit resultiert aus der Tatsache, daß der RM für ein SingleTask-System kon- zipiert wurde und daher auch die ganzen Performance-Killer eines MultiTask- Systems nicht mit sich herumschleppen muß. Damit entfallen im RM:
- alle Schutz- und Privilegmechanismen (CPL, IOPL, usw.) - der im PM bzw. VM übliche Zeitscheiben-Krieg ;) konkurrierender Tasks - zeitintensive Vorgänge wie Task Switches, Segmentregister-Loads, usw.
Der Hauptvorteil des PM im Zusammenhang mit RM/Flat4G ist dessen Fähigkeit, Speicher linear adressieren zu können. Ab 80286 kann im PM auf diese Weise direkt auf bis zu 16 MByte, ab 80386 sogar auf bis zu 4 GByte RAM zugegrif- fen werden. Und das (speziell ab 80386) noch dazu mit einer Geschwindigkeit, die für segmentierte Speichermodelle nahezu unerreichbar ist.
Um unter DOS im RM auf RAM oberhalb der 640 KByte-Grenze zugreifen, existie- ren zwar auch einige Möglichkeiten (Treiber bzw. Programme, wie z.B. HIMEM. SYS oder EMM386.EXE), aber diese besitzen i.d.R. den gravierenden Nachteil, daß sie auf einer der Spezifikationen XMS oder EMS (LIM) aufsetzen.
EMS hat den Nachteil, daß für die CPU immer nur ein 64 KByte-Fenster des ge- samten oberen RAMs "sichtbar" ist. Der Zugriff wird durch das ständige Ein- und Ausblenden beim "Verschieben" des Fensters quälend verlangsamt.
XMS behebt zwar das Manko der kleinen 64 KByte-Fenster, erlaubt den Zugriff auf das RAM aber auch nur indirekt (über Handles). XMS hat zwar den Vorteil, daß man RAM-Blocks für den Eigenbedarf sperren kann (schützt vor Konflikten mit anderen Programmen), ist (gemessen an linearer Adressierung) aber auch nicht gerade das Gelbe vom Ei.
Doch zurück zum Topic... ;)
Ok, wie realisieren wir nun RM/Flat4G?
- Als erstes benötigen wir eine Routine, die sicherstellt, daß mindestens eine 80386 CPU vorhanden ist. Nur dann gewinnen wir auch die Performance- Vorteile eines 32bit-Busses (80286er haben nur einen 16bit-Datenbus) und haben gleichzeitig die Gewissheit, daß die CPU auch mehr als 16 MByte RAM (was die Maximalgrenze für einen 80286 wäre) adressieren kann. Letzteres bringt natürlich nur dann Nutzen, solange auch mehr als 16 MByte RAM vor- handen sind... ;)
Wer gerade keine Routine zur Hand hat, kann zur CPU-Identifikation z.B. LSG0001, oder die im Zusammenhang mit RM/Flat4G wohl eher geeignete (weil darauf zugeschnittene) Methode aus LSG0005 oder LSG0006 einsetzen (prüft in einem Rutsch auf 386+ im RM).
- Desweiteren müssen wir aus mehreren Gründen, die im weiteren Verlauf noch offensichtlich werden dürften, sicherstellen, daß sich die CPU im RM (und nicht etwa im PM oder VM) befindet.
Es gibt zwar verschiedene softwareseitige Wege, die CPU einfach wieder zu- rück in den RM zu "zwingen", aber erstens ist das eine aufwendige Sache, die sehr tiefgreifende Kenntnisse erfordert (will man alle erdenklichen Situationen berücksichtigen), und zweitens zieht man sich mit einer sol- chen Aktion i.d.R. auch schnell den Zorn des Anwenders zu... >-)
Zumeist lässt der Anwender ja nicht ohne triftigen Grund, oder nur so zum Spaß ;) irgendwelche Multitasker rumhampeln, seien es nun Memory Manager, die auch von anderen Programmen benötigt werden, oder was auch immer...
Für mich persönlich ist das Grund genug, die CPU nicht in den RM zwangszu- versetzen, daher wird in meinen RM/Flat4G-Sources (s. LSG0005 und LSG0006) auch lediglich abgefragt, ob sich die CPU im RM befindet und, falls nicht, das Programm mit einem entsprechenden Hinweis beendet.
Wen trotzdem interessiert, wie man die CPU vom PM oder VM grundsätzlich zurück in den RM bekommt (sorry, nur ein paar Möglichkeiten und die auch nur kurz angerissen, wird sonst endlos):
PM -> RM
Falls Paging aktiviert ist, zuerst folgendes: Sprung zu vom Paging un- berührtem Code (linear=physikalisch), Paging-Bit (CR0.PG) löschen, und schließlich Page Table aus Page Directory löschen (über CR3).
Sprung in Segment, dessen Limit RM-üblich ist (64 KByte). DS, ES, FS, GS und SS mit Selektoren laden, die auf Deskriptoren mit RM-Charakter- istik zeigen (Present, Writable, ExpandUp, Granularity=Byte, Limit 64 KByte). IRQs sperren (CLI), Protected-Bit löschen (CR0.PE), PQ-Flush, IDTR mit Adresse der RM-Interruptvektortabelle laden, IRQs freigeben (STI), Segmentregister-Init für weiteren gewünschten RM-Betrieb.
VM -> RM
TaskSwitch via TaskGate, oder IRET bei EFLAGS.NT=1 (EFLAGS des neuen Tasks muß natürlich VM=0 haben).
Eine andere Möglichkeit wäre z.B. das Auslösen eines Interrupts oder einer Exception über Interrupt oder Trap Gate zu einem Non-conforming CPL0-Codesegment... &)
Doch zurück zum Punkt: Wie stellen wir nun fest, ob der RM aktiv ist?
Ganz einfach: Mit dem 80286 wurde u.a. auch das MSW (Machine Status Word) eingeführt. Bit 0 vom MSW, das sogenannte PE-Bit (Protection Enable Bit) zeigt an, ob sich die CPU momentan im RM (MSW.PE = 0) oder im PM bzw. VM (MSW.PE = 1) befindet. Für das Lesen/Beschreiben des MSW erhielt der 286er zwei Instructions: SMSW r|m16 (Lesen) bzw. LMSW r|m16 (Schreiben).
Mit dem 80386 wurde das MSW auf 32bit erweitert und erhielt für diese neue Gesamtbreite auch einen neuen Namen: CR0 (Control Register 0). Da es sich bei CR0 also im Prinzip nur um ein erweitertes MSW handelt, kann auch nach wie vor mit SMSW/LMSW darauf zugegriffen werden (allerdings aus Kompatibi- litätsgründen natürlich nur auf die unteren 16 Bits). Für den Zugriff auf die volle Breite erhielten der 80386 und dessen Nachfolger folgende Ins- tructions: MOV r32, CR0 (Lesen) bzw. MOV CR0, r32 (Schreiben).
Im Zusammenhang mit RM/Flat4G ist imo SMSW die bessere Variante, da SMSW eine unprivilegierte Instruction ist, und somit auch dann problemlos im PM ausgeführt werden kann, wenn das Programm keinen CPL0 besitzt (was in un- serem Fall ja durchaus sein könnte). Ein MOV r32, CR0 würde im PM bei ei- nem CPL > 0 zu einer allgemeinen Schutzverletzung (#GP[0]) führen... &)
- Der nächste Punkt, um den wir uns kümmern müssen, ist ein Kompatibilitäts- Handicap, daß alle CPUs ab 80286 mit sich herumschleppen, um den RM 100% abwärtskompatibel zum 8086 betreiben zu können:
Der RM (s. dazu auch BAW0012) wurde für 8086 CPUs implementiert. Eine 8086 CPU verfügt lediglich über 20 Adressleitungen, kann also maximal bis zu 1 MByte (2^20 - 1) RAM adressieren. Adressierungen über diesen Punkt hi- naus schlägt die CPU wieder auf den Anfang um (sogenannter ¯WrapAround®).
CPUs ab 80286 besitzen jedoch bekanntlich mehr als 20 Adressleitungen, al- so muß ein Mechanismus dafür sorgen, daß im RM keine Zugriffe über diese ursprünglich maximal 20 Adressleitungen hinaus erfolgen, ansonsten ist es Essig mit der Abwärtskompatibilität...
Die Lösung für dieses Problem ist das sogenannte A20-Gate. CPUs ab 80286 sperren während der CPU-Initialisierung für den RM über dieses Gate ein- fach die 21ste Adressleitung (A20) und somit auch alle höherliegenden.
Für uns bzw. RM/Flat4G bedeutet dies natürlich, daß wir A20 wieder frei- schalten müssen, um die CPU zumindest schon einmal aus dem grundsätzlichen 1 MByte-Dilemma des RM zu befreien. Damit ist zwar die letzte Barriere, die Überwindung der 64 KByte-Segmentgrenzen, noch nicht durchbrochen, doch dazu später mehr...
Bleibt die Frage: Wie bekommen wir A20 denn frei?
Nun, wer keine Lust hat, das Rad neu zu erfinden, kann sich der Funktionen 3..6 des XMS-Standards (Global bzw. Local A20 Enable/Disable) bedienen. Da jedem dem XMS-Standard folgenden Treiber A20-Steuerungsfunktionen imple- mentiert werden müssen, muss der Programmierer nur noch über INT 2f.4300H prüfen, ob ein solcher Treiber (wie z.B. HIMEM.SYS) aktiv ist und kann ihn dann (nach Ermittlung des Treiber-Vektors (INT 2f.4310H)) über FAR CALLs ggf. benutzen. Diese Technik der A20-Steuerung wird in LSG0005 (RM/Flat4G und XMS) verwendet.
HIMEM.SYS dürfte heutzutage zwar bei den meisten Anwendern defaultmäßig in Betrieb sein (alleine schon, um das DOS-Kernel in die HMA auslagern zu können), aber sicher ist auf unserer Welt bekanntlich gar nix... ;)
Für den Fall, daß kein XMS-Treiber aktiv ist, haben wir nun zwei Möglich- keiten: Entweder wir geben einen entsprechenden Hinweis aus, und beenden das Programm, oder wir schalten selbst das A20-Gate über den Keyboard Con- troller frei...
Ja, wie? Keyboard Controller? &)
Aehm, fragt mich nicht. Ich habe keine Ahnung, warum die Entwickler alles mögliche in die Tastaturports gequetscht haben, was überhaupt nichts mit der Tastatur an sich zu tun hat... ;)
Nur eines: Auch wenn die Programmierung des A20-Gates sehr simpel ist (das erfordert im Prinzip nur ein Port 64H = d1H, Port 60H = dfH), so funktio- niert diese Vorgehensweise nur bei 100% AT-kompatiblen Controllern, anson- sten kann die Programmierung mit obigen Werten natürlich etwas vollkommen anderes bewirken.
Wer generell mißtrauisch ist ;), oder sich im Fall der Verwendung von exo- tischen Keyboard Controllern zumindest eine relative Gewissheit verschaf- fen möchte, daß die direkte A20-Freischaltung von Erfolg gekrönt war, kann nachprüfen, ob sich durch die Umschaltung das WrapAround-Verhalten verän- dert hat.
Um zu testen, ob der WrapAround aktiv ist, vergleicht man einfach eine be- liebige Anzahl Bytes in der HMA mit ihren WrapAround-Korrespondenten, d.h. z.B. ffff:0010H bis ffff:010fH mit 0000:0000H bis 0000:00ffH. Stimmen alle Bytes überein, ist es relativ wahrscheinlich ;), daß WrapAround aktiv ist, anderenfalls sollte die A20-Freischaltung funktioniert haben.
Beispiellösungen zur direkten A20-Steuerung bzw. WrapAround-Prüfung sind übrigens in LSG0006 (RM/Flat4G und RAW) enthalten.
- Zuletzt müssen wir nun noch dafür sorgen, daß wir das lästige und lahme segmentierte Speichermodell, genauer die 64KByte-Segmentgrenzen, loswerden und stattdessen endlich linear adressieren können... 8)
Das Problem hierbei ist: Die lineare Adressierung funktioniert bekanntlich nur im Protected Mode. Wie bringen wir nun die CPU dazu, diese nur für den ursprünglichen RM erforderlichen 64 KByte-Limits zu vergessen?
Nun, das ganze Geheimnis basiert, wie so oft bei Intel CPUs ;), auf einem für lange Zeit undokumentierten Feature. Irgendwann (wann genau, weiß ich nicht mehr. Ich persönlich erfuhr davon jedenfalls so etwa um den Jahres- wechsel 86/87) sickerte ¯inoffiziell® ;) durch, daß die Intel CPUs bei der Rückkehr vom PM in den RM ihre internen Bereiche, u.a. auch die für das Descriptor-Caching, nicht leeren.
Um zu verstehen, was das bedeutet, benötigt man etwas mehr Hintergrundwis- sen. Es ist (vereinfacht gesagt) so: Wird im PM ein Segmentregister mit einem Selector geladen, ermittelt die CPU über das GDTR den entsprechenden Descriptor aus dem GDT und legt ihn in einem, für "normale" Instructions "unsichtbaren" (d.h. nicht erreichbaren), Cache ab. Da sich derartige Des- criptor Caches quasi in der CPU, und nicht im vergleichsweise lahmen RAM befinden, kann die CPU natürlich alle nachfolgenden Zugriffe (Base, Limit, AccessRights, usw.) auf zugehörige Segmente bzw. deren Deskriptoren auch wesentlich schneller durchführen.
Für RM/Flat4G ist die durch das Descriptor-Caching im PM erzielbare Ge- schwindigkeitssteigerung zwar irrelevant, aber der Fakt, daß die CPU diese Caches bei der Rückkehr in den RM nicht löscht, ist unser Schlüssel dazu, den 64 KByte-Riegel überhaupt knacken zu können... B)
Alles was wir im Prinzip tun müssen ist, einen entsprechenden GDT zu defi- nieren, die Adresse des GDT ins GDTR zu laden, in den PM zu schalten, ein oder mehrere Segmentregister mit Selektoren auf einen 4 GByte-Descriptor zu laden und dann abschließend wieder zurück in den RM zu schalten... ;)
Wer sich mit dem PM noch nicht so recht auskennt, dem sei in diesem Zusam- menhang die Lektüre der Instructions LAR und LGDT im Topic BAW0009 empfoh- len. Desweiteren dürften auch LSG0005 und LSG0006 evtl. hilfreich sein...
- Oops, einen wichtigen Hinweis hätte ich beinahe vergessen: Es ist zwingend erforderlich, daß vor dem Umschalten von einem in den anderen Prozessormo- dus (egal ob vom RM in den PM, oder umgekehrt) der Prefetch-Queue der CPU geleert wird (sogenannter PQ-Flush).
Der PQ-Flush ist deshalb so wichtig, weil die CPU höchstwahrscheinlich be- reits einige Instructions vordekodiert hat und für den Execution Stream bereithält. Würde in diesem Zustand der Prozessor umgeschaltet, erhielte man allenfalls einen kostenlosen Crash-Test... 8)
Auslösen lässt sich ein PQ-Flush auf verschiedene Weise, am einfachsten jedoch über eine NEAR JMP-Instruction. PQ-Flushs sind übrigens nur bis einschließlich P5 erforderlich, ab P6 (=PPro) sind zumindest MOV CR0, r32 und ähnliche Instructions serialized, d.h. ein P6 löscht nach Ausführung solcher speziellen Instructions den Prefetch-Queue selbst.
So, das wars fürs Erste zum RM/Flat4G. Viel Spaß beim Experementieren... ;)
Interrupts
Topic noch zu vergeben.
Exceptions
Juergen Thelen 2:2450/55.5:
Anmerkung: Ich verwende in diesem Topic immer die Bezeichnung ISR (Interrupt Service Routine) für den Programmteil, der bei Auslösung einer Exception die Kontrolle erhält. Andere Autoren mögen in diesem Zusammenhang vielleicht eher die Begriffe Exception o. Interrupt Handler benutzen. Es ist aber das gleiche gemeint... ;)
Exceptions werden immer dann ausgelöst, wenn es zu Situationen kommt, durch die Gefahr für Konsistenz und Integrität eines Programmes oder Betriebssys- tems besteht, bzw. wenn die CPU einfach nicht mehr weiterarbeiten könnte.
In solchen Situationen werden die ISRs festgelegter Interrupts angesprungen, die sich dann dem entsprechenden Fehler annehmen sollen. Was die jeweilige ISR macht, hängt vom Betriebssystem bzw. der Programmiersprache und dem Pro- grammierer eines Programmes ab, und kann nicht verallgemeinert werden.
Weiter ist zu beachten, daß Intel zwar eigentlich die Interrupts 0 bis 20H für CPU-interne Zwecke (wie z.B. Exception Handling) definierte, sich aber z.B. IBM nicht um diese Konvention kümmerte. IBM benutzt diese Interrupts gleichzeitig für gänzlich andere Zwecke (Hardware-IRQs 0..7, BIOS-Schnitt- stellen, usw.). Daraus ergaben sich die heute bekannten Doppelbelegungen der einzelnen Interrupts.
Im Protected Mode werden Exceptions zusätzlich noch in drei verschiedene Kategorien unterteilt:
- Faults Returnadresse (CS:EIP) auf dem Stack zeigt auf die verursachende Instruction für den Fault.
- Traps Returnadresse zeigt hinter die verursachende Instruction für die Trap. Ausgenommen sind alle kontrollübergebenden Instructions, wie z.B. JMP - in diesen Fällen enthält die Returnadresse die Zieladresse der Kontrollübergabe (z.B. des JMPs)...
- Aborts Returnadresse undefiniert.
Bei Protected Mode Exceptions, die sich auf ein bestimmtes Segment beziehen, schiebt der Prozessor vor der Returnadresse einen Fehlercode auf den Stack der ISR. Dies gilt für die Exceptions 10 bis 13:
Fehlercode bei PM Exceptions mit Segmentbezug +++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1| | | | | | | | | | | | | | | | | |f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0|f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| | | | | | | | | Obere 14 Bit des von der |T|I|E| | reserviert | Exception betroffenen |I|D|X| | | Segment Selectors |D|T|T| +-----------------------------+---------------------------+-+-+-+
EXT = 1, wenn Exception-Ursache außerhalb des Programmes IDT = 1, wenn Index sich auf IDT Gate Descriptor bezieht TID = 0: Fehlercode bezieht sich auf GDT (nur wenn IDT-Bit = 0) 1: Fehlercode bezieht sich auf LDT (nur wenn IDT-Bit = 0)
Bei den Interrupts 14 (#PF, Page Fault) und 18 (#MC, Machine Check) weicht das verwandte Fehlercodeformat vom oben beschriebenen ab (Beschreibung des Formates siehe Dokumentation des jeweiligen Interrupts).
Bei den Interrupts 8 (#DF, Double Fault) und 17 (#AC, Alignment Check) wird als Fehlercode lediglich eine Null auf Stack geschoben.
Bei allen anderen Interrupts (0, 1, 3, 4, 5, 6, 7 und 16) wird KEIN Fehler- code auf dem Stack plaziert.
Im folgenden eine Auflistung, welche Ursachen die Auslösung einer Exception haben kann und was aus meiner Sicht sonst noch über den jeweiligen Interrupt wissenswert erscheint:
Interrupt 0, Divide Error (PM: Fault) [#DE] -------------------------------------------
- Divisor bei DIV oder IDIV ist gleich Null
- Quotient nach DIV oder IDIV passt nicht in Zielregister
- Resultat einer Division ist 80H oder 8000H (nur 8086/88)
Hinweis: bei 8086/8088 CPUs zeigt die Returnadresse auf dem Stack HINTER, bei allen Nachfolgern dagegen AUF die verursachende Instruction.
Interrupt 1, Single Step Exceptions (PM: Fault/Trap) [#DB] ----------------------------------------------------------
- Single Step (PM: Trap)
Wird durch Setzen des TF (Trap-Flag) ausgelöst. TF wird beim Einsprung in die ISR gelöscht (Trap-Handler würde sonst selbst im Einzelschritt- verfahren durchlaufen).
Sollten gleichzeitig Hardware-Interrupts (IRQ 0..15) anliegen, sorgt die interne Prioritätenverteilung dafür, daß erst Returnadresse und Flags von Interrupt 1 auf den Stack gelegt werden, dann das TF gelöscht wird und im Anschluss daran die ISR des IRQ gerufen wird. Die ISR von Inter- rupt 1 erhält die Kontrolle erst nach Abarbeitung etwaiger IRQ-ISRs.
- Data Address Breakpoint (PM: Trap)
Break-Condition erfüllt.
- Instruction Address Breakpoint (PM: Fault)
Break-Condition erfüllt.
- General Detect (PM: Fault)
Nur Intel's ICE: Nächste Instruction würde auf ein Debugregister zugrei- fen, daß momentan von ICE benutzt wird.
- Taskswitch Breakpoint Trap
Break-Condition erfüllt.
Bei Interrupt 1 wird kein Fehlercode auf den Stack gelegt. Zur Identifika- tion des Auslösers bedient man sich der Debug-Register DR0..7 (s. BAW0007)
Interrupt 2, Non-maskable Interrupt (NMI) -----------------------------------------
Zählt nicht zu den herkömmlichen Exceptions. Wird ausgelöst, wenn der NMI# Pin durch Hardware angesprochen wird.
Vom Verhalten her könnte man NMIs als Traps bezeichnen (die Returnadresse zeigt HINTER die verursachende Instruction).
Sollten während der ISR-Abarbeitung weitere NMIs auftreten, werden diese bis zum IRET ignoriert. Ab 286+ wird jedoch zumindest ein nachfolgender NMI gepuffert und nach erfolgtem IRET direkt behandelt.
Sollten die maskierbaren IRQs in der NMI-ISR nicht mittels CLI unterbunden worden sein, können diese die NMI-ISR natürlich unterbrechen.
Interrupt 3, Breakpoint (PM: Trap) [#BP] ----------------------------------------
Dieser Interrupt wird durch die Instruction INT 3 (Opcode ccH) ausgelöst.
Da es sich hierbei um eine 1-Byte-Instruction handelt, eignet sie sich sehr gut zum Setzen unkonditionaler Breakpoints. Viele Debugger benutzen dieses Verfahren, um Register- oder Variableninhalte zu einem bestimmten Ablaufzeitpunkt eines Programmes anzeigen zu können.
Vorsicht, Stolperfalle: Interrupt 3 zählt zu den Traps, d.h. die Return- Adresse zeigt HINTER den Auslöser (INT 3). Vor der Rückkehr also Original- Opcode zurückschreiben (sowieso ;) und eIP der Returnadresse um eins ver- mindern... &)
Interrupt 4, INTO Overflow Detected (PM: Trap) [#OF] ----------------------------------------------------
Dieser Interrupt wird ausgelöst, wenn das OF-Flag (Overflow Flag) gesetzt ist, während die Instruction INTO (Opcode ceH) ausgeführt wird.
Dadurch erhält der Programmierer die Möglichkeit, das Overflow-Handling seines Programmes in eine ISR auszulagern und kann sich innerhalb des Pro- grammes speicherintensive Gerüste von JO/JNO/JMP Instructions sparen.
Interrupt 5, BOUND Range exceeded, 186+, (PM: Fault) [#BR] ----------------------------------------------------------
Dieser Interrupt wird von der Instruction BOUND ausgelöst, wenn ein über- prüfter Index (signed Word bzw. DWord (386+)) außerhalb der angegebenen Grenzen liegt.
BOUND eignet sich also z.B. bestens, um Range Checks für Array-Zugriffe zu realisieren.
Interrupt 6, Invalid Opcode, 286+, (PM: Fault) [#UD] ----------------------------------------------------
- Ein ungültiger Opcode wurde ausgeführt.
- Ein gültiger Opcode wurde mit ungültigem Operanden ausgeführt (z.B. LDS oder segmentübergreifender JMP mit Register als Quelle).
- Das LOCK Prefix wurde zusammen mit einer nicht zulässigen Instruction benutzt (386+).
Vorsicht: Die Opcodes d6H und f1H sind zwar von Intel nicht definiert, lö- sen aber trotzdem KEINEN Interrupt 6 aus... ;)
Bei Interrupt 6 wird kein Fehlercode auf den Stack gelegt.
Dieser Interrupt wurde früher von vielen 386er BIOSs benutzt, um undoku- mentierte Befehle wie z.B. LOADALL zu emulieren.
Interrupt 7, Coprocessor not available, 286+, (PM: Fault) [#NM] ---------------------------------------------------------------
- Dem Prozessor liegt eine ESC Instruction zur Ausführung vor, aber in CR0 (bzw. MSW) ist das EM- (Emulation), oder TS-Bit (Task switched) gesetzt.
- Dem Prozessor liegt eine WAIT Instruction zur Ausführung vor, gleichzei- tig sind aber das MP- (Monitor Coprocessor) und TS-Bit (Task switched) in CR0 (bzw. MSW) gesetzt.
Interrupt 8, Double Fault, 286+, PM Only (Abort) [#DF] ------------------------------------------------------
Ein Double Fault wird ausgelöst, wenn zwei Exceptions aufeinanderfolgen, die bestimmten Exceptionklassen-Kombinationen entsprechen.
Der Prozessor unterscheidet drei Exceptionklassen: Die Benign Exception, die Contributory Exception und den Page Fault. Der nachfolgenden Tabelle kann entnommen werden, welcher Interrupt welcher Klasse zugeordnet ist:
Interrupt Klasse
0, Divide Error Contributory 1, Single Step Exceptions Benign 2, Non-maskable Interrupt Benign 3, Breakpoint Benign 4, INTO Overflow Detected Benign 5, BOUND Range Exceeded Benign 6, Invalid Opcode Benign 7, Coprocessor not available Benign 8, Double Fault - 9, Coprocessor Segment Overrun Benign 10, Invalid Task State Segment Contributory 11, Segment not present Contributory 12, Stack Fault Contributory 13, General Protection Fault Contributory 14, Page Fault Page Fault 15, - - 16, Coprocessor Error Benign 17, Alignment Check Benign 18, Machine Check Benign
Die in der nachfolgenden Tabelle aufgeführten Klassenkombinationen führen schließlich zu einem Double Fault:
Klasse der 1. Exception Klasse der 2. Exception
Contributory Exception Contributory Exception Page Fault Contributory Exception Page Fault Page Fault
Bei allen anderen Klassenkombinationen können die Exceptions vom Prozessor einfach nacheinander abgearbeitet werden.
Wird ein Double Fault ausgelöst, schiebt der Prozessor zwar einen Fehler- code auf den Stack der Double Fault ISR, aber der ist leider immer Null und somit imo völlig überflüssig...
Durch Forcieren einer weiteren Exception innerhalb einer Double Fault ISR lässt sich ein sogenannter "Triple Fault" auslösen. Bei einem Triple Fault geht der Prozessor in den ShutdownMode, d.h. es wird ein CPU-Reset ausge- löst, wobei der Prozessor auch in den Real Address Mode zurückkehrt...
Interrupt 9, Coprocessor Segment Overrun, nur 386 mit 387, (PM: Abort) ----------------------------------------------------------------------
Dieser Interrupt wird ausgelöst, wenn der Coprozessor versucht, auf einen Speicherbereich außerhalb Segment- bzw. Page-Grenzen zuzugreifen.
Ab 486+ wird diese Exception nach Interrupt 13 (General Protection Fault) umgeleitet.
Solange der Coprozessor nicht resetted worden ist, befindet sich das aus- lösende Programm in einem undefinierten Zustand, es kann also nicht fort- gesetzt werden. Um fortsetzen zu können, bleibt einer Interrupt 9 ISR nur, eIP zu sichern und die FPU durch FNINIT zu RESETen, da das Programm sonst bis zum jüngsten Tag auf Daten der FPU wartet, die nie kommen werden... ;)
Interrupt 10, Invalid Task State Segment, 286+, PM/VM only (Fault) [#TS] ------------------------------------------------------------------------
Dieser Interrupt wird ausgelöst, wenn während eines Taskswitch auf einen Task mit ungültigem TSS (Task State Segment) umgeschaltet wird. Ein ungül- tiges TSS kann verschiedene Ursachen haben:
- TSS Limit ist kleiner als 43 (286) bzw. 103 (386+)
- Ungültiger LDT Selector oder LDT nicht vorhanden
- CS, DS, ES, FS, oder GS Selector außerhalb Table Limit
- DS, ES, FS oder GS ist kein lesbares Segment
- CS Selector bezieht sich nicht auf ein Code Segment
- DPL eines non-conforming CS <> neuem CPL
- DPL eines conforming CS > neuer CPL
- SS ist kein beschreibbares Segment
- SS Selector RPL <> CPL
- SS DPL <> neuem CPL
Einen entsprechenden Fehlercode schiebt der Prozessor bei TSS Faults auf den Stack.
Hinweis: TSS Faults können sowohl im alten, als auch in einem neuen Task ausgelöst werden, je nachdem, ob der Taskwechsel bereits voll- ständig vollzogen wurde, oder nicht.
Solange das TR (Task Register) nicht entsprechend upgedatet ist, wird die Exception im alten Task ausgelöst.
Um sicherzustellen, daß die Interrupt 10 ISR ein gültiges TSS hat, sollte die ISR vorzugsweise selbst als Task betrieben werden, der via Task Gate ausgelöst werden kann. Und natürlich darf die ISR vor dem Rücksprung nicht vergessen, das Busy Bit des neuen Tasks zu löschen... ;)
Interrupt 11, Segment not present, 286+, PM/VM only (Fault) [#NP] -----------------------------------------------------------------
Interrupt 11 wird ausgelöst, wenn der Prozessor merkt, daß das Present-Bit eines Descriptors gleich Null ist. Das kann passieren:
- beim Versuch einen Gate Descriptor zu benutzen, der auf ein nicht vor- handenes Segment zeigt.
- beim Versuch CS, DS, ES, FS oder GS zu laden. Das gilt übrigens nicht für SS, in diesem Fall würde ein Stack Fault (Interrupt 12) ausgelöst.
- beim Versuch mit LLDT den LDT zu laden, es sei denn, dies geschieht während einem Taskswitch. Dann würde Invalid TSS Fault (Interrupt 10) ausgelöst.
Der Prozessor schiebt vor der Auslösung einen Fehlercode auf den Stack.
Bei diesem Fault kann die Programmausführung trotzdem fortgesetzt werden, wenn die Interrupt 11 ISR das Present-Bit setzt und zurückspringt.
Allerdings ist dabei zu beachten, daß der Fault auch während eines noch nicht abgeschlossenen Taskswitches ausgelöst worden sein kann. In diesem Fall könnte es sein, daß der Prozessor noch nicht alle Segmentregister auf ihre Gültigkeit geprüft hat und nachfolgende Speicherzugriffe irgendwo ins Nirwana laufen. Folglich sollte eine Interrupt 11 ISR alle Segmente selbst auf Gültigkeit prüfen, bevor sie das Present-Bit setzt und dem neuen Task wieder die Kontrolle übergibt. Anderenfalls kann es später zu kuriosen General Protection Faults kommen, deren Ursachen viel schwerer zu lokali- sieren sind.
Der einfachste Weg die Zulässigkeit der Segmentregister zu prüfen, ist ein simples PUSH/POP mit jedem Register, da der Prozessor nur POPs mit gülti- gen Daten in ein Segmentregister zulässt.
Ein anderer Weg ist es, die Interrupt 11 ISR als eigenständigen Task zu betreiben. Der durch den Rücksprung aus der ISR ausgelöste Taskswitch erle- digt alle erforderlichen Prüfungen.
Interrupt 12, Stack Fault, 286+, (PM: Fault) [#SS] --------------------------------------------------
Im Real und V86 Mode wird Interrupt 12 ausgelöst, wenn versucht wird, auf ein Word an Adresse SS:ffffH zuzugreifen.
Im Protected Mode kann Interrupt 12 aus zwei Gründen ausgelöst werden:
- Limit-Verletzung durch eine Instruction die auf das SS zugreift, d.h. die Instruction führt zu einem Unter- oder Überlauf des Stacks, oder greift außerhalb des gültigen Stackbereiches zu. Zu den auslösenden Instructions zählen somit PUSH/POP, ENTER/LEAVE und alle mittels MOV adressierten Stackzugriffe (SS:Override, eBP, usw.).
- Present-Bit des SS-Descriptors ist gleich Null. Erkannt wird dies bei einem Task Switch, CALL (anderer CPL), RET (anderer CPL), LSS, sowie bei MOVs/POPs in Verbindung mit SS.
Der Prozessor schiebt auch bei diesem Interrupt einen Fehlercode auf den Stack. Bei Present-Bit gleich Null oder CALLs, die zu einem Stacküberlauf führen, ist der fragwürdige Selector im Fehlercode enthalten, sonst ist der Fehlercode gleich Null.
HINWEIS: Normalerweise zeigt die Returnadresse bei Faults ja immer auf die auslösende Instruction. Wurde der Stack Fault aber während eines Task Switch aufgrund Present-Bit gleich Null ausgelöst, zeigt die Returnadresse bereits auf die erste Instruction des neuen Tasks!
Bei der Auslösung wegen Present-Bit gleich Null während eines Task Switch, kann die Programmausführung wieder aufgenommen werden, wenn die ISR das Present-Bit setzt und zurückspringt. Vor einem Resume sind allerdings die gleichen Dinge zu beachten (Segmentprüfung), wie unter Interrupt 11 ent- sprechend beschrieben.
Interrupt 13, General Protection Fault, 286+, (PM: Fault/Abort) [#GP] ---------------------------------------------------------------------
Im Real und V86 Mode wird Interrupt 13 wegen folgender Ursachen ausgelöst:
- OperandWrap: Eine Instruction versucht auf einen Speicheroperanden zu- zugreifen, der teilweise in einem und teilweise im nächsten Segment liegt, d.h. Word-Zugriffe an Offset ?S:ffffH, oder DWord-Zugriffe an den Offsets ?S:fffdH, ?S:fffeH oder ?S:ffffH.
- InstructionWrap: Eine Instruction selbst beginnt in einem, endet aber erst im nächsten Segment.
Im Protected Mode kann die Auslösung Interrupt 13 viele Ursachen haben, da alle Schutzverletzungen, die nicht eigens durch eine Exception gecovered werden, hier zusammenlaufen:
- Versucht in ein ReadOnly DATA oder ein EXECUTABLE Segment zu schreiben.
- Versucht aus einem ExecOnly-Segment zu lesen.
- Versucht Code in einem NonExec-Segment auszuführen.
- Versucht Speicherzugriffe über DS, ES, FS oder GS auszuführen, wenn das Segmentregister einen Null-Selector enthält.
- Versucht DS, ES, FS, GS oder SS mit dem Selector eines EXECUTABLE Seg- mentes zu laden.
- Versucht SS mit dem Selector eines ReadOnly-Segmentes zu laden (außer es handelt sich um einen TSS-Selector während eines Task Switch, dann kommt es zu einem Interrupt 10).
- Versucht DS, ES, FS, GS oder SS mit dem Selector eines SYSTEM Segmen- tes zu laden.
- Versucht CR0 mit PG-Bit = 1 und PE-Bit = 0 zu setzen (versucht Paging im RM zu aktivieren...;))
- Limit-Verletzung beim Referenzieren eines Descriptor Tables.
- Limit-Verletzung beim Einsatz von CS, DS, ES, FS oder GS.
- Umschalten auf einen beschäftigten (busy) Task.
- Verletzung der Privileg-Regeln.
- V86 Mode: Interrupt/Exception wurde über Trap/Interrupt Gate auf einen CPL <> 0 ausgelöst.
- Instruction zu lang (286 > 10 Bytes, 386+ > 15 Bytes).
- Versucht ein reserviertes Bit in einem Spezialregister (z.B. CR4) zu beschreiben (P5+).
Der Prozessor schiebt auch bei diesem Interrupt einen Fehlercode auf den Stack. Sollte das Laden eines Descriptors den Interrupt ausgelöst haben, ist der entsprechende Selector im Fehlercode enthalten, ansonsten ist der Fehlercode gleich Null.
Interrupt 14, Page Fault, 386+, PM/VM Only (Fault) [#PF] --------------------------------------------------------
Dieser Interrupt wird ausgelöst, wenn das PG-Bit in CR0 gesetzt ist, und die CPU während der Übersetzung einer Adresse (linear in physikalisch) einen der folgenden Umstände bemerkt:
- Der anfordernde Code hat eine zu niedrige Privilegstufe, um auf die gewünschte Page zugreifen zu dürfen.
- Das Present-Bit des Page Directory bzw. des Page Table Eintrages, der übersetzt werden soll, ist gleich Null (Segment not present).
- Ein reserviertes Bit in einem Page Table Entry, Page Directory Entry oder Page Directory Pointer wurde gesetzt (P5+).
Der Prozessor schiebt auch bei diesem Interrupt einen Fehlercode auf den Stack. Allerdings weicht der Aufbau vom üblichen Fehlercode ab:
Fehlercode bei Page Fault +++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1| | | | | | | | | | | | | | | | | |f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0|f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| | | | | | | | |R| | | | | |S|U|W| | | reserviert |V|/|/| | | |D|S|R|P| +-------------------------------------------------------+-+-+-+-+
P = 1, wenn Page Fault durch zu niedrige Privilegstufe 0, wenn Page Fault durch Present-Bit gleich Null W/R = 1, wenn Schreibzugriff den Page Fault ausgelöst hat 0, wenn Lesezugriff den Page Fault ausgelöst hat U/S = 0, wenn Page Fault im SupervisorMode auftrat 1, wenn Page Fault im UserMode auftrat RSVD = 1, wenn Page Fault wegen P, W/R oder U/S = 0, wenn Page Fault wegen Beschreiben reservierter Bits im Page Directory (vorausgesetzt CR4.PSE=1 oder CR4.PAE=1)
Wird ein Page Fault ausgelöst, können zusätzlich die Controlregister CR2 und CR3 ausgelesen werden, um weitere Infos zu erhalten. Siehe dazu auch Topic BAW0007 (CPU-Registersatz). In diesem Zusammenhang noch eine Beson- derheit: Wird während der Ausführung des Handlers ein weiterer Page Fault ausgelöst, schiebt die CPU statt des oben beschriebenen Fehlercodes direkt CR2 auf den Stack... &)
HINWEIS 1:
Wenn ein Betriebssystem aufgrund seiner Programmierung dies zulässt, kann ein Page Fault auch während eines Task Switch ausgelöst werden. Die Auslösung kann dann durch folgende Operationen des Prozessors verursacht worden sein:
- Schreiben des Status im TSS des alten Task - Lesen des GDT (CPU braucht TSS-Descriptor für neuen Task) - Lesen des neuen TSS (um die Segment-Descriptoren zu prüfen) - Lesen der LDT (Bestätigung der neuen Segmentregister)
In den ersten beiden Fällen zeigt die Returnadresse auf dem Stack noch auf die Instruction, die den Task Switch verursachte, in den beiden an- deren Fällen bereits auf die nächste auszuführende Instruction des neuen Task.
Um Ärger zu vermeiden, empfiehlt es sich daher imo, Interrupt 14 ISRs immer als eigenständige Tasks zu betreiben, die via Task-Gate ausgelöst werden... ;)
HINWEIS 2:
Einige Programmierer benutzen auch heute noch die Codesequenz
mov ax, [MyStackSeg] mov ss, ax mov sp, [MyStackTop]
um einen neuen Stack einzurichten. Ab 386+ ist diese Vorgehensweise mit der Gefahr verbunden, daß SS zwar gesetzt werden konnte, der SP-Zugriff aber einen Page Fault auslöst. Dadurch verliert der Stackpointer SS:SP (oder auch SS:ESP) natürlich seine Integrität.
Wird die Interrupt 14 ISR nun via Trap oder Interrupt Gate gerufen und besitzt die gleiche Privilegierung wie die ISR selbst, benutzt die CPU diesen inkonsistenten Stackpointer trotzdem, was natürlich die wunder- samsten Ergebnisse zu Tage bringen kann... ;)
Sicherer ist imo auch hier wieder der Betrieb der Interrupt 14 ISR als eigenständiger Task, oder wenn Trap/Interrupt Gates vorgezogen werden, die Initialisierung des Stack über die Instruction LSS.
Interrupt 16, Coprocessor Error, 286+, (PM: Fault) [#MF] --------------------------------------------------------
Erzeugt ein FPU-Befehl (ESC-Instruction) einen Fehler, wird dieser der CPU über einen bestimmten Pin (z.B. ERROR# (FPU 387) oder FERR# (CPU 486+)) gemeldet.
Dieser Pin wird erst vor der Ausführung der jeweils NÄCHSTEN ESC- oder WAIT-Instruction abgefragt. Ein FPU-Fehler kann also nur dann zur Auslö- sung von Interrupt 16 führen, wenn mindestens eine weitere ESC- oder WAIT- Instruction auf die eigentlich fehlerhafte Instruction erfolgt.
Ist ein Fehlersignal einmal erkannt, reagiert die CPU je nach Zustand von CR0.NE und des IGNNE# Pins (Ignore Numeric Error) verschieden:
- Ist CR0.NE=1 wird IGNNE# ignoriert. Die Kontrolle wird in diesem Fall direkt an die Interrupt 16 ISR abgegeben.
- Ist CR0.NE=0 und IGNNE# nicht aktiv, wird die CPU solange angehalten, bis ein Interrupt von der externen Hardware kommt. Erst dann wird die Kontrolle an die Interrupt 16 ISR weitergegeben.
- Ist CR0.NE=0 und IGNNE# aktiv, wird die Exception von der CPU einfach ignoriert und das Programm ohne Fehlerbehandlung fortgesetzt.
Interrupt 17, Alignment Check, 486+, PM/VM only (Fault) [#AC] -------------------------------------------------------------
Interrupt 17 wird ausgelöst, wenn die CPU auf einen nicht ausgerichteten (unaligned) Operanden zugreift (z.B. Word, Selector oder 16:16 Pointer nicht auf 0MOD2-, oder DWord oder 16:32 Pointer nicht auf 0MOD4-Boundary).
Der Interrupt wird nur in mit CPL3 laufenden Programmen ausgelöst.
Grundsätzliche Voraussetzung für den Alignment Check ist natürlich, daß die CPU auch im Alignment Mode arbeitet (CR0.AM = 1)... &)
Bei Auslösung von Interrupt 17 wird als Fehlercode eine Null auf den Stack geschoben (wozu auch immer die gut sein soll)... ;)
Interrupt 18, Machine Check, P5+, (PM: Abort) [#MC] ---------------------------------------------------
HINWEIS: Bei Interrupt 18 handelt es sich um eine modellspezifische Ex- ception, d.h. Intel unterstützt sie bei Nachfolgern des P5 unter Umständen nur noch teilweise, in anderer Form, oder eventuell gar nicht mehr...
Auf dem PPro (in dieser FAQ durchgehend als P6 bezeichnet) exis- tiert sie jedenfalls noch (wenn auch bereits anders implementiert als beim P5)... ;)
Dieser Interrupt wird bei verschiedenen Hardwarefehlern ausgelöst (voraus- gesetzt CR4.MCE = 1).
Die Fehlerursache kann im Handler über die MSRs (Model specific registers) MSR.MCA (Machine Check Address) und MSR.MCT (Machine Check Type) ermittelt werden (siehe dazu BAW0007, CPU-Registersatz).
Hochsprachen-Schnittstellen
a) ASM <-> Pascal (TP/BP), Lennart Groetzbach 2:2432/601.13:
Auch bei der Benutzung von Hochsprachen gibt es immer Fälle, in denen ein paar Zeilen Assembler von Nöten sind. Ich möchte kurz die Möglichkeiten für Pascal vorstellen...
Bei Pascal beziehe ich mich auf die Implementierung von Borlands Turbo Pascal (TP), da ich auf diesen Zugriff besitze.
Wenn ich von Prozeduren spreche treffen die Aussagen auch immer auf Funk- tionen zu, da diese prinzipiell den selben Aufbau besitzen.
1. Grundlagen
Es gibt zwei Möglichkeiten in Pascal Assemblermodule einzubinden. Zum einen kann man fertig kompilierte Objektdateien einbinden, oder inner- halb Pascals Assemblerblöcke einfügen.
Bei beiden Möglichkeiten gibt es einige Konventionen, die auf jeden Fall beachtet werden sollten...
- die Register DS, SS, SP und BP sollten nach Abarbeitung der ASM-Pro- zeduren mit ihren ursprünglichen Werten restauriert werden.
- Funktionen liefern Werte über folgende Register zurück:
+--------------------+-------------+ | BYTE, BOOLEAN | AL | +--------------------+-------------+ | INTEGER, WORD | AX | +--------------------+-------------+ | LONGINT | DX:AX | +--------------------+-------------+ | REAL | DX:BX:AX | +--------------------+-------------+ | EXTENDED, DOUBLE | ST | +--------------------+-------------+ | Zeigertypen | DX:AX | +--------------------+-------------+
- Parameter werden auf dem Stack übergeben. Sie werden "von links nach rechts" auf den Stack gelegt. Bei VAR Parametern werden Segment und Offset, (in dieser Reihenfolge!) auf den Stack gelegt.
Wenn man einen Parameter von einem Byte Größe übergibt (also Byte oder Boolean) wird dieser als Word auf den Stack gelegt, d.h. das 1. Byte hat den Wert 0 und das zweite den Wert des eigentlichen Para- meters. Dies geschieht, weil nur Words auf den Stack gelegt werden können.
1.1 .OBJ Files einbinden
Als Speichermodell benutzt Pascal LARGE. Variablen, Prozeduren und Funktionen, auf die das Assemblermodul Zugriff haben soll, werden im ASM-Quelltext durch das Schlüsselwort "EXTRN" deklariert.
Variablen, Prozeduren und Funktionen auf die Pascal zugreifen können soll, werden im ASM-Quelltext als PUBLIC deklariert.
Wenn man nun auf ein Assemblermodul zugreifen möchte, muss der Compi- ler zuerst einmal wissen, um was es sich handelt. Aus diesem Grunde wird der Funktions- oder Prozedurkopf angegeben und mit dem Schlüssel- wort EXTERNAL versehen. Danach wird der Compilerbefehl $L aufgerufen und der Name des Assemblermoduls angegeben.
Hier ein Beispiel für die Zusammenarbeit:
///// ASMMODUL.ASM ///////////////////////////////////////////////////
.model large .286 .data
extrn tausche : Byte
.code
public exchange
exchange proc near ; wurde die Boolvariable cmp tausche, 0 ; gesetzt? je @exit ; Nein, dann Ende
enter 0, 0 ; erzeugt Stackframe. für ; Zugriff auf Variablen mov si, [bp+4] ; liest Offset d. 1. Param. mov al, byte ptr [si] ; liest Wert d. 1. Param. mov di, [bp+8] ; liest Offset d. 2. Param. mov ah, byte ptr [di] ; liest Wert d. 2. Param. xchg al, ah ; vertauscht Werte mov [si], al ; und schreibt sie zurück mov [di], ah leave ; löscht Stackframe @exit: ret exchange endp
end
///// ASMMODUL.ASM - Ende //////////////////////////////////////////
///// ASM_TEST.PAS ///////////////////////////////////////////////////
uses Crt; var aa, bb : Byte; tausche : Boolean;
procedure exchange(var a, b : Byte); External; {$L asmmodul.obj}
begin ClrScr; aa := 1; bb := 3; tausche := FALSE; exchange(aa,bb); WriteLn(aa); WriteLn(bb); tausche := TRUE; exchange(aa,bb); WriteLn(aa); WriteLn(bb); end.
///// ASM_TEST.PAS - Ende //////////////////////////////////////////
Noch ein paar Worte zur Erklärung. Die Boolvariable "tausche" wird von der Assemblerprozedur abgefragt, ob die Werte der beiden Parameter ge- tauscht werden sollen. Das ist natürlich nicht sehr elegant, es soll auch nur die Benutzung von PUBLIC verdeutlichen.
Im Assemblermodul werden dann DI und SI mit den Offsets der beiden VAR-Parameter geladen. Dann werden die Werte nach AL und AX gelesen, vertauscht und zurückgeladen.
Zu beachten ist, daß Zeiger nur per SI, DI und BX adressiert werden können! Alle anderen Register sind nicht dafür vorgesehen.
Der Befehl "ENTER" legt den sog. Stackframe an, indem lokale Variablen und Übergabeparameter verwaltet werden. Der Frame muss auf jeden Fall am Ende der Prozedur mit "LEAVE" wieder entfernt werden, da sonst SS, das Stacksegment, mit einem falschen Wert die Prozedur beendet.
Der Aufruf für ENTER lautet: ENTER <Anz. lokale Vars> , <Tiefe>
Benötigt man also z.B. eine lokale Variable vom Typ Word, muss für die Anzahl 2 (!) eingegeben werden, da ein Word ja zwei Byte entspricht!
Die Tiefe ist für die Verschachtelung von Prozeduren zuständig. Ist dieser Wert <> 0, werden im Stackframe Adressen anderer Frames gespei- chert.
Hier für unsere Prozedur...
. . . . +--------------------------------+ | Segment von b | [bp + 10] +--------------------------------+ | Offset von b | [bp + 8] +--------------------------------+ | Segment von a | [bp + 6] +--------------------------------+ | Offset von a | [bp + 4] +--------------------------------+ | Rücksprung Adr. | [bp + 2] +--------------------------------+ | Alter Wert von BP | [bp + 0] -----> bp + +--------------------------------+ + | | 1. Lokale Variable (Word) | [bp - 2] -----> sp | + +--------------------------------+ + . . . .
Die lokale Variable wird in "exchange" nicht benutzt, sie soll nur verdeutlichen, daß die erste lokale Variable immer(!) per [bp - 2] und der erste Parameter immer per [bp + 4] adressiert wird. Dann würde die Anweisung auch folgendermassen lauten :
ENTER 2, 0
1.2 Der integrierte Assembler
Anstatt einen "selbständigen" Assembler zu bemühen, kann man natürlich auch auf den integrierten Assembler (BASM) von Turbo Pascal zurück- greifen.
Dies bietet den Vorteil, dass man den gesamten Quellcode vor sich hat; aber BASM unterstützt nur 286er Quellcode, so muss man auf eine Reihe nützlicher Befehle und auf die erweiterten Register leider verzichten.
Hier nun die Prozedur "exchange" noch einmal in BASM geschrieben...
///// PROZEDUR exchange //////////////////////////////////////////////
procedure exchange(var a, b : Byte); Assembler; asm cmp tausche, 1 je @exit
les di, a mov al, [di] les si, b mov ah, [si]
xchg al, ah
mov es:[di], al mov es:[si], ah @exit: end;
///// PROZEDUR exchange - Ende //////////////////////////////////////
Anstatt die Parameter über den Stackframe anzusprechen, kann man jetzt nutzen, daß Pascal die Variablennamen bekannt sind.
Variablen des Hauptprogramms und "normale" Parameter kann man bequem über ihre Namen adressieren. Da bei VAR-Parametern die Adresse auf den Stack gelegt wird, ist es dort ein wenig schwieriger.
Mit dem Befehl "LES DI,A" wird die Adresse von "A" in das Registerpaar ES:[DI] geladen. Das gleiche geschieht auch für "B". Diesen Befehl gibt es auch für das Register DS (LDS halt).
Danach werden die nun vertauschten Werte wieder in die Zielregister zurückgeladen. Zu beachten ist, daß ich in diesem Fall zwei Mal ES als Segmentregister benutze und das erste Zielsegment somit überschreibe. Dies ist möglich, da Pascal seine Variablen in einem gesonderten Daten Segment (DSeg) aufbewahrt. Bei anderen Zeigern, z.B. auf Interrupts etc. ist dies nicht anzuraten...
Noch einmal kurz zu dem 286er Code den BASM vom Benutzer erwartet. Man kann dieses Hindernis umgehen, jedoch verliert der Quellcode damit wieder seine Lesbar- und Verständlichkeit...
Um die erweiterten Register anzusprechen, muss man ein "DB 66h" einem Befehl voranstellen. Dem 286er unbekannte Befehle können natürlich als Opcodes geschrieben werden. Hier ein paar Beispiele:
db 66h xor ax, ax (* entspricht xor eax,eax *)
db 66h mov es:[di], ax (* entspricht mov es:[di], eax *)
db 66h db $ab (* entspricht stosd *)
1.3 Die Inline Anweisung
Diese Anweisung ist noch ein Vermächtnis aus alten Pascaltagen und wird nur noch aus Kompatibilitätsgründen mitgeführt. Mit ihr können Assembler Opcodes direkt eingegeben werden, was dem Quelltext immer eine kryptische Note verleiht!
Ich gebe hier kein Beispiel an, da ich nicht über meiner OpCode Liste grübeln möchte und verweise auf die Online Hilfe, die deutlich genug zeigt, daß Inline Assembler keine lesbaren Erfolge hervorbringt.
1.4 "Assembler" und "Interrupt"
Kurz noch ein paar Worte zu den optionalen Schlüsselwörtern des Pro- zedurkopfes...
Falls man eine Prozedur in reinem Assembler schreiben will, kann man sich mit dem Schlüsselwort "ASSEMBLER" im Prozedurkopf einen BEGIN-END Block sparen.
Das Schlüsselwort "INTERRUPT" bewirkt, daß die Register AX, BX, CX, DX, SI, DI, DS, ES und BP (in dieser Reihenfolge) auf den Stack gelegt werden. Außerdem wird ein Stackframe angelegt und DS mit dem Datenseg- ment initialisiert. Die Prozedur wird zudem mit einem "IRET" beendet.
1.5 Anmerkungen
Falls man mit dem Gedanken spielt, andere Programme per Exec aufzuru- fen, oder Interrupts umbiegen will, sollte man vorher auf jeden Fall die Prozedur "SWAPVECTORS" aufrufen, da Pascal sich einiger Interrupts bemächtigt und sie umleitet.
Damit hoffe ich, daß ich alle relevanten Möglichkeiten Assembler in Pas- cal einzubinden angesprochen habe.
VGA-Registersatz
Juergen Thelen 2:2450/55.5:
Vorab: Eingangs sollte direkt erwähnt werden, daß man in diesem Topic keine unmittelbare Anleitung zur Realisierung sogenannter ¯Tweaked Modes® oder ¯Weird Modes® finden wird. Dazu zählen beispielsweise der auf Videomodus 13H basierende Mode X, (???x???x256 mit ? Pages), auf Vi- deomodus 03H basierende ??x??x16 Textmodi, usw...
Mode X besitzt mittlerweile sein eigenes Topic (siehe BAW0020). Ein Topic für ¯Weird Modes® wird es vielleicht irgendwann einmal in einer zukünftigen FAQ-Release geben.
Auf die zahlreichen möglichen grafischen Effekte, die sich über die VGA realisieren lassen, werde ich (bis auf etwas Scrolling) bewußt nicht näher eingehen. Alleine die Beschreibung mir bekannter Effekte würde wohl locker ca. 1000 DIN A5 Seiten eines Buches füllen, und ich kenne imo höchstens ein Fünftel aller existierenden... ;)
Sollte jemand speziell auf der Suche nach Monochrominfos zur VGA/SVGA sein, wird auch er in diesem Topic nicht viel finden. Da heutzutage monochrome Videomodi eher selten geworden sind, lag die Konzentration auf farbigen Videomodi. Sorry... ;)
Ähnliches gilt für Videomodi, Ports und Register, die nicht zum Stan- dardsatz der originalen VGA gehören. Die Fülle der Einzelspezialitä- ten jedes Grafikchips bzw. jeder Modellvariante auf dem laufenden zu halten, ist imo ein Fulltime-Job. Es kommen dauernd neue Grafikchips mit immer neuen Funktionen auf den Markt, bei denen sich, wenn über- haupt, höchstens mal die Videomodi an einen bestimmten VESA-Standard halten (von Ports/Registern reden wir besser erst gar nicht)... ;)
Zudem verlangen die Chiphersteller i.d.R. so um die 50 USD für jede Chip-Dokumentation (Erfahrungswerte (ATI, Tseng, u.v.m.)). Daß kaum ein Programmierer Notiz von den neuen Modi/Features nimmt, dürfte an- gesichts der derzeit ca. 200 existierenden, verschiedenen Basis-Chips (und die dann in x modellspezifischen Varianten) dann wohl kaum noch jemand verwundern... $>
Nun, wer will, kann es ja SciTech nachtun (ja, die UniVBE/SDD-Leute).
Die sind, wie übrigens auch nahezu jeder namhafte Grafikchip-Herstel- ler, offizielles Mitglied der VESA, und dürften daher wohl kaum Pro- bleme gehabt haben, an die Dokus zu kommen. Für diejenigen, die sich den Mindestmitgliedschaftsbeitrag der VESA (1.375 USD p.a., umsatzab- hängig) leisten können, eventuell sogar eine lohnende Sache... $)
Oops, ich schweife ab. Kommen wir zurück zum Topic... ;)
Kompatibilität wird also im GraKa-Bereich offensichtlich leider nicht gerade groß geschrieben. Jeder Hersteller kocht hier sein eigenes Süppchen, um alle möglichen Videomodi und/oder spezielle Fähigkeiten zu verwirklichen und sich von der Konkurrenz abzuheben.
Das einzige, auf das sich Programmierer heute verlassen können ist, daß fast jede zur Zeit erhältliche Farbgrafikkarte auf der originalen VGA-Karte von IBM, und somit deren Registersatz und Standard-Videomodi (00..13H) aufsetzt.
Dadurch erschließt sich dem Programmierer eine gesunde Basis, auf der noch universelle Low-Level-Programmierung machbar ist, denn schließlich wollen wir ja Top-Performance, und die lässt sich nun mal nur Low-Level erreichen..
Bei Auflösungen/Farben außerhalb des originalen VGA-Bereiches sieht es mit der Low-Level-Programmierung (und damit auch mit der Top-Performance) leider sehr schlecht aus...
Auf Single Task Systemen (wie z.B. DOS) orientiert man sich zum Zwecke uni- verseller Programmierung imo am besten am VESA-Standard. Will man zumindest in die Nähe von Low-Level Performance kommen, ist dazu aber auch bereits der aktuelle Grafikstandard VBE 2.0 (VESA BIOS Extension 2.0) erforderlich.
Die VBE 2.0 (bzw. deren API) ist übrigens im Topic BAW0021 detailliert doku- mentiert. Eines der herausstechendsten Merkmale der VBE 2.0 ist das sogenan- nte LFBM (Linear Frame Buffer Model). Für dieses Model ist ebenfalls ein ei- genständiges Topic vorhanden (siehe BAW0022).
Was Multitasking Systeme (wie z.B. W95) betrifft: Wer auf solchen Systemen auf Top-Performance aus ist, sollte seinen Wunschtraum schnellstens wieder begraben. Soviele Vorteile das Multitasking auch immer haben mag, es ist und bleibt Performancekiller #1. Kommen dann auch noch OS-Nadelöhre wie z.B. das DirectX von W95 (und/oder die teilweise megalahmen OEM-Treiber) dazu, hilft wohl nur noch das Warten auf SunSparcs oder Crays zu Low-End Preisen... ;)
Abschließend noch ein kleiner Hinweis zu allen Registern, die für das soge- nannte Scrolling zuständig sind: Da ich mich noch bestens erinnern kann, daß einen die Gegenläufigkeit optischer und technischer Auswirkungen beim Pro- grammieren der entsprechenden Register anfänglich ganz schön verwirren kann, ist die optische Auswirkung immer durch Großbuchstaben (also LINKS, usw.) besonders hervorgehoben.
Wie auch immer, here goes... ;)
Die Bilddarstellung aller VGA-kompatiblen Grafikkarten wird über fünf Haupt- und einige wichtige Nebenbausteine gesteuert:
Bausteine bzw. Register Portadresse(n)
- ATC (Attribute Controller) 3c0H, 3c1H - CRTC (Cathode Ray Tube Controller) 3d4H, 3d5H - DAC (Digital/Analog Converter) 3c6H, 3c7H, 3c8H, 3c9H - GDC (Graphics Data Controller) 3ceH, 3cfH - TS (Timing Sequencer) 3c4H, 3c5H
º Feature Control Write Register 3daH [W] º Feature Control Read Register 3caH [R] º Input Status #0 Register 3c2H [R] º Input Status #1 Register 3daH [R] º Miscellaneous Output Write Register 3c2H [W] º Miscellaneous Output Read Register 3ccH [R] º Video Subsystem Enable Register 3c3H
Bevor wir nun zu den Registern kommen, für die Einsteiger kurz noch ein paar Erläuterungen zu Begriffen/Abkürzungen, auf die man bei der Programmierung der VGA-Karte immer wieder stossen wird:
- PFreq (Pixel Frequency), Dot Frequency, Dot Rate, Pixelfrequenz
Die PFreq gibt an, wieviele Punkte pro Sekunde die Grafikkarte in einem bestimmten Videomodus erzeugt bzw. der Monitor darzustellen hat. Je nach Videomodus sind dabei Werte zwischen 25 und 135 MHz üblich.
- HFreq (Horizontal Scan Frequency), Zeilenfrequenz
HFreq bezeichnet die Anzahl Zeilen pro Sekunde, die die Grafikkarte in einem bestimmten Videomodus erzeugt bzw. der Monitor darzustellen hat. Auch hier hängt der Wert wieder vom Videomodus selbst ab. Üblich sind Frequenzen zwischen 15 und 80 kHz.
- VFreq (Vertical Refresh Frequency), vertikale Bildwiederholfrequenz
Die VFreq sagt aus, wieviele Bilder pro Sekunde die Grafikkarte in einem bestimmten Videomodus erzeugt bzw. der Monitor darzustellen hat. Die für VFreq gängigen Werte liegen im Bereich 50 bis 100 Hz (non-interlaced).
- Bandwidth, Bandbreite, Frequenzbereich
Bandbreiten sagen etwas über die generelle Leistungsfähigkeit eines Mo- nitors bzw. einer Grafikkarte hinsichtlich einer bestimmten Frequenzart aus. Sie bezeichnen die Minimal- und Maximalwerte, die das Gerät für ei- ne spezifische Frequenzart leisten kann.
Wie wichtig diese Bandbreitenwerte sind (speziell die des Monitors), wi- rd spätestens klar, wenn man sich einmal vor Augen hält, was passiert, wenn eine Grafikkarte einen Videomodus mit beispielsweise einer Pixel- frequenz vom 50 MHz erzeugt, der Monitor in dieser gleichen Frequenzart aber lediglich eine Bandbreite von 15 bis 30 MHz zu leisten vermag.
Die Bilddaten würden viel schneller von der Grafikkarte erzeugt und an den Monitor gesendet, als der Monitor sie überhaupt verarbeiten kann. Im Extremfall, bzw. bei längerer Aufrechterhaltung dieses Zustandes, dürfte sich der Monitor dann irgendwann quietschend und rauchend verabschieden (sofern er keinen Übertaktungsschutz o.ä. besitzt).
- Video-RAM, VRAM, Bildschirmspeicher
Beim Video-RAM handelt es sich um einen Speicherbereich, in dem kodierte Bildinformationen gespeichert werden. Ein bestimmter Teil des Video-RAM, die sogenannte Fetch Area, wird vom CRTC für jeden physikalischen Bild- schirmaufbau aufs Neue ausgelesen, in Zusammenarbeit mit den anderen VGA-Controllern dekodiert, in Farbsignale konvertiert und schließlich an den Monitor gesandt (was sich bildlich in Form der Display Area nieder- schlägt).
Das Video-RAM wird normalerweise an den Segmentgrenzen a000H, b000H oder b800H (abhängig von Videomodus und Monitor) in den CPU-Adressraum einge- blendet. Dieses Einblendungsfenster ist dabei generell 32 bzw. 64 KByte groß, auch wenn ein Videomodus vielleicht viel weniger benötigt (Video- modus 03H, 80x25x16, benötigt z.B. nur 4000 Bytes/Seite), oder die Karte eventuell wesentlich mehr Video-RAM bietet (die originale VGA hatte be- reits 256 KByte; heute sind 2 bis 4 MByte durchaus üblich).
- Fetch Area
Bezeichnung für einen bestimmten Teil innerhalb des gesamten Video-RAMs, aus dem der CRTC die Daten für die Bilderzeugung zu entnehmen hat. Die Fetch Area kann man sich bildlich als ein ¯über dem Video-RAM verschieb- bares rechteckiges Fenster der Dimension Breite x * Höhe y® vorstellen.
Die Startposition der Fetch Area (m.a.W. die obere linke Ecke) innerhalb des Video-RAMs lässt sich (ebenso wie z.B. deren Breite) über verschie- dene CRTC-Register beeinflussen, wodurch zahlreiche grafische Effekte, wie z.B. das sogenannte Smooth Scrolling, überhaupt erst möglich werden.
- CCU (Character Clock Unit)
Der CRTC verwaltet viele horizontale Parameter nicht, so wie vielleicht angenommen, pixelweise, sondern in der Einheit Zeichen (Character). Das gilt übrigens nicht nur für Text-, sondern auch für alle Grafikmodi. ;)
Wieviel Pixeln ein Character (eine CCU) entspricht, ist grundsätzlich von einem Bit des Timing Sequencers, dem TS.CMR.CCU8-Bit (3c4.01H[0]), abhängig. Ist das Bit gesetzt, entspricht eine CCU acht, ist es dagegen gelöscht, neun Pixeln.
- Character Counter
Hierbei handelt es sich um ein internes 16bit-Zählregister (0..) des TS (Timing Sequencers), in dem die CCUs beim horizontalen Zeilenaufbau mit- gezählt werden.
Der Character Counter lässt sich zwar offiziell weder beschreiben, noch auslesen, aber nichtsdestotrotz hat er einen erheblichen Einfluss auf das gesamte horizontale Videotiming.
Der Wert des Character Counters wird ständig mit dem Inhalt verschieden- er CRTC-Register verglichen. Bei Übereinstimmung werden dann automatisch verschiedene Aktionen, wie z.B. das Einleiten des HRetrace, ausgelöst.
- Line Counter
Auch beim Line Counter handelt es sich um ein internes, und für den Pro- grammierer offiziell "unsichtbares", 16bit-Zählregister (0..) im TS.
Es enthält immer die Nummer der aktuellen Scan Line. Vorsicht, Stolper- falle: Diese Nummer bezieht sich auf Zeilen ingesamt (inkl. des Overscan und Blanking Frames), was nicht immer und unbedingt auch der korrespon- dierenden Zeilennummer im Video-RAM entsprechen muß... ;)
Der Wert des Line Counters wird, ähnlich wie der des Character Counters, ständig mit dem Inhalt verschiedener CRTC-Register verglichen. Bei die- sen Registern handelt es sich aber natürlich nicht mehr um Register, die das horizontale, sondern das vertikale Videotiming, wie z.B. den Zeit- punkt für die Auslösung des VRetrace, steuern.
- Rasterstrahl
Vereinfachende Zusammenfassung der drei Kathodenstrahlen des Monitors, die gebündelt die, mit jeweils drei Phosphorpartikeln (Rot, Grün, Blau) gefüllten, Löcher der Monitorlochmaske abtasten bzw. beschiessen und da- durch die Löcher kurzzeitig in entsprechenden Farben aufleuchten lassen.
- Display Enable
Hierbei handelt es sich um ein Signal, während dessen Anliegen der CRTC Daten aus der Fetch Area als Farbquellen interpretiert und über den ATC bzw. den DAC ermittelte RGB-Werte ausgibt.
Ist Display Enable deaktiviert und liegt auch kein Blanking Signal an, dient als Farbquelle das Overscan Color Register (ATC.OCR, 3c0.11H).
Liegt ein Blanking Signal an, ist der RGB-Partikelbeschuss völlig einge- stellt, d.h. der Rasterstrahl bewegt sich zwar weiterhin, aber es wird keine Farbe mehr dargestellt (Eindruck schwarz).
- Display Area
Bezeichnung für die rechteckige Fläche des Monitors, auf die die Bild- informationen aus der (im Video-RAM befindlichen) Fetch Area sozusagen projiziert werden.
Um die Display Area herum befindet sich der sogenannte Overscan Frame. Der Overscan Frame schließlich wird wiederum vom Blanking Frame umfasst.
Die Lage der drei Bereiche lässt sich mit der nachfolgenden Mini-Source auch optisch verdeutlichen (VGA-kompatible Karte und Farbmonitor natür- lich vorausgesetzt):
mov ax, 3 int 10H ; Videomodus 3 (80x25x16) aktivieren
mov ax, 0b800H ; Video-RAM Segment (in Farbmodi) mov es, ax xor di, di ; Video-RAM Offset 0 mov ax, 2020H ; Leerzeichen, schwarz auf grün mov cx, 07d0H ; 2000 = 80 * 25 mal ASCII-Code und Attribut cld rep stosw ; Fetch Area grün färben
mov dx, 03daH in al, dx ; ATC-WriteFlipFlop auf Indexmode
mov al, 31H ; 11H + 20H (Overscan mit gesetztem PRMD-Bit) mov dx, 03c0H out dx, al ; Overscan Register anwählen mov al, 1 out dx, al ; Overscan Frame auf blau setzen
Nach der Ausführung dieses Codes sollte die Display Area in grün, der Overscan Frame in blau und der Blanking Frame in schwarz zu sehen sein.
Die Außengrenzen des Blanking Frames sind allerdings meistens nicht bzw. nur schlecht zu erkennen. Sollte der Monitor über entsprechende Regler verfügen, hilft oft schon horizontales und/oder vertikales Stauchen der Bildausgabe, da sich grobe Stauchungen auf vielen Monitoren durch Farb- abweichungen im Blanking Frame bemerkbar machen (sehr dunkles grau).
Hat man genug gesehen, reicht ein ¯mov ax, 3 ; int 10H®, oder auch ein MODE CO80 auf DOS-Ebene, um die Auswirkungen der Mini-Source wieder zu beseitigen... B)
- Overscan Frame
Gesamtbegriff für horizontale und vertikale Austastlücken, die sich bei entsprechender Farbgebung als oberer, unterer, linker und rechter Bild- rahmen um die Display Area präsentieren würden.
Im Normalfall nimmt man den Overscan Frame nicht wahr, weil das Overscan Color Register (ATC.OCR, 3c0.11H) standardmäßig in allen Videomodi den Farbindex für die Farbe schwarz (RGB: 0, 0, 0) enthält.
Da der Overscan Frame an den Innenkanten des (wiederum ihn umfassenden) Blanking Frame endet, im Blanking Frame aber keinerlei Farbe dargestellt wird (was i.d.R aber wie schwarze Farbe wirkt) entsteht der Eindruck ei- nes einzigen durchgehenden Rahmens um die Display Area.
- Blanking Frame
Gesamtbegriff für horizontale und vertikale Bereiche des Monitorbildes, die einen Rahmen um den Overscan Frame bilden.
Die Zeiträume des horizontalen/vertikalen Blankings (und Retraces) wer- den vom CRTC genutzt, um bereits die Farbdaten für die nächste Scan Line vorzubereiten.
- HRetrace (Horizontal Retrace), horizontaler Strahlenrücklauf
Als HRetrace wird das Zurücksetzen des Rasterstrahles vom Zeilenende an den Anfang der nächsten Scan Line bezeichnet. Er wird ausgelöst, wenn der Character Counter eine, durch ein CRTC-Register festgelegte, CCU er- reicht.
- VRetrace (Vertical Retrace), vertikaler Strahlenrücklauf
Als VRetrace bezeichnet man das Zurücksetzen des Rasterstrahles aus der unteren rechten in die obere linke Bildschirmecke. Der Zeitpunkt, zu dem ein VRetrace eingeleitet wird, ist abhängig vom Erreichen einer bestim- mten (in einem CRTC-Register festgelegten) Scan Line im Line Counter.
Ok, ich denke, für den Einstieg dürfte das vorerst genügen. Tiefergreifende Details werde ich evtl. einmal in einem zukünftigen Topic erläutern.
ATC, Attribute Controller -------------------------
Der ATC dient, wie sein Name schon andeutet, überwiegend der Verwaltung von Farb- und Textattributen. Zusätzlich ist er u.a. auch für andere Dinge, wie z.B. das horizontale Scrolling verantwortlich.
Programmiert wird der ATC über Register der Ports 3c0H und 3c1H.
Grundsätzliche Voraussetzung für sowohl das Beschreiben, als auch das Aus- lesen von Registern des ATCs ist, daß sich Port 3c0H im Indexmodus befindet.
Dazu sollte man wissen, daß Port 3c0H mit einem WriteFlipFlop arbeitet, d.h. jedes Beschreiben des Ports führt zu einem Wechsel zwischen Indexmodus und Datenmodus (und umgekehrt). Die einzige allgemeingültige Methode, um diesen Port in den Indexmodus zu versetzen, ist kurioserweise das Auslesen von Port 3daH (Input Status #1 Register).
Wie dem auch sei, befindet sich Port 3c0H einmal im Indexmodus, kann ihm je- denfalls durch ein Byte die gewünschte Registernummer übergeben werden. Be- schreiben lässt sich das ausgewählte Register dann durch das Senden eines zweiten Wertes an Port 3c0H. Soll das ausgewählte Register dagegen ausgele- sen werden, ist ein Lesezugriff über Port 3c1H erforderlich.
HINWEIS: Da beim ATC neben der üblichen Datenverarbeitungsdauer zusätzlich auch noch einige Zeit für das Umschalten zwischen Index- und Daten- modus erforderlich ist, sollte speziell bei der Kombination schnel- le CPU & langsame VGA der nächste ATC-Portzugriff frühestens 250 ns auf den vorangegangenen erfolgen.
Langsame VGAs sind zwar heute eher die Ausnahme, but who knows.. ;)
ATC, Port 3c0H, Registerwahl ++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4 3 2 1 0| | | | | | | | |P| | | | |R| | | | |M| | |?|?|D| REG_IDX | +-+-+-+---------+
REG_IDX HINWEIS: Wenn unklar ist, welchen Modus das WriteFlipFlop dieses Ports gerade hat, empfiehlt sich das Auslesen von Port 3daH (Input Status #1 Register), um den Port 3c0H vor der Registerwahl in den Indexmodus zu zwingen.
Gewünschte Registernummer zum nachfolgenden Beschreiben/Lesen:
00H PAL0, Palette Register 0 01H PAL1, Palette Register 1 02H PAL2, Palette Register 2 03H PAL3, Palette Register 3 04H PAL4, Palette Register 4 05H PAL5, Palette Register 5 06H PAL6, Palette Register 6 07H PAL7, Palette Register 7 08H PAL8, Palette Register 8 09H PAL9, Palette Register 9 0aH PALA, Palette Register 10 0bH PALB, Palette Register 11 0cH PALC, Palette Register 12 0dH PALD, Palette Register 13 0eH PALE, Palette Register 14 0fH PALF, Palette Register 15 10H MCR, Mode Control Register 11H OCR, Overscan Color Register 12H CPER, Color Plane Enable Register 13H HPPR, Horizontal Pixel Panning Register 14H CSR, Color Select Register
PRMD Palette Register Modify Disable.
Über das PRMD-Bit wird gesteuert, ob der Inhalt der ATC.PALn Re- gister (3c0.00H..0fH) von der CPU geändert werden darf (0), oder nicht (1).
Da der ATC (speziell in Videomodi mit 16 oder weniger Farben) dauernd die den Fetch Area Daten entsprechenden Palettenregister auslesen muß, um die darzustellenden Farben zu ermitteln, kann natürlich nicht auch gleichzeitig noch die CPU schreibend auf die selben Register zugreifen dürfen.
Die Folge wären Konflikte optischer und/oder technischer Natur.
Daher stellt der ATC einfach die Interpretation der Fetch Area Daten ein, sobald man der CPU durch ein gelöschtes PRMD-Bit das Zugriffsrecht auf die Palettenregister zuweist. Die Folge davon ist, daß der ATC die Display Area nur noch in einer Farbe dar- stellt, nämlich in der, die im Overscan Color Register (ATC.OCR, 3c0.11H) festgelegt wurde. Aufgehoben wird dieser Zustand erst wieder dann, wenn der ATC die Kontrolle zurückerhält, d.h. wenn die nächste ATC-Registerwahl mit gesetztem PRMD-Bit erfolgt.
ATC.PALn (PAL0..PALF), Palette Register 0..15 (3c0.00H..0fH) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6|5 4|3 2 1 0| | | | | | | |DAC_IDX| +---+---+-------+
DAC_IDX Die Farbzuweisung an ein Palettenregister geschieht auf VGA/SVGA Karten nicht mehr durch direkte RGB-Wertzuweisung, sondern über einen Farbindex, der auf ein bestimmtes der 256 DAC-Register in der DAC-Tabelle zeigt (Tabellenaufbau siehe DAC-Beschreibung).
Wie die gestrichelten Linien im vorstehenden Registerbild evtl. schon ahnen lassen, besitzt DAC_IDX keine konstante Breite, son- dern variiert, je nach Videomodus, zwischen 4, 6 und 8 Bit.
In Videomodi mit 256 Farben (s. auch ATC.MCR.8BPP, 3c0.10H[6]) ist es einfach: DAC_IDX ist in diesem Fall 8bit breit, d.h. eine hier gemachte Angabe entspricht exakt dem Farbindex (der Nummer des DAC-Registers) in der DAC-Tabelle.
Anders sieht die Sache bei Videomodi aus, die mit 16 Farben oder weniger bestückt sind. In diesem Fall muß der Farbindex aus den unteren 4 bzw. 6 Bit des DAC_IDX und den ATC.CSR.PB45/PB67-Bits (3c0.14H[0..1/2..3]) zusammengesetzt werden.
Wie die Komponenten zu kombinieren sind, bzw. interpretiert wer- den, hängt primär vom ATC.MCR.CS45-Bitzustand (3c0.10H[7]) ab:
ATC.MCR.CS45 = 0 ATC.MCR.CS45 = 1
+-- Farbindex --+ +-- Farbindex --+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |7 6|5 4 3 2 1 0| |7 6|5 4|3 2 1 0| | | | | | | | | P | | | P | P | | | B | | | B | B | | | 6 | | | 6 | 4 | | | 7 | DAC_IDX | | 7 | 5 |DAC_IDX| +---+-----------+ +---+---+-------+
Wie man sieht, haben die PBnn-Bits bei Videomodi mit 16 oder we- niger Farben sozusagen Granularitäts-Charakter. Sie bestimmen, welcher der 64er- (CS45 = 0) bzw. 16er-Blocks (CS45 = 1) in der DAC-Tabelle angesprochen werden kann, oder anders ausgedrückt, welches der 256 DAC-Register als Basis für DAC_IDX dienen soll.
HINWEIS: ATC.OCR.DAC_IDX (3c0.11H) wird genau nach dem gleichen Schema gehandhabt, wie es hier für ATC.PALn.DAC_IDX be- schrieben steht.
HINWEIS: Schreibzugriffe auf Palettenregister sind nur möglich, wenn bei der ATC-Registerwahl das PRMD-Bit (Bit 5) ge- löscht war, d.h. wenn Port 3c0H im Indexmode mit einem Wert zwischen 00H und 0fH beschrieben wurde.
ATC.MCR, Mode Control Register (3c0.10H) [RW] +++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | |C|8|U| |F|9|M| | |S|B|S| |L|E|O|G| |4|P|C| |S|Q|N|F| |5|P|R|?|H|8|O|X| +-+-+-+-+-+-+-+-+
GFX Grafikmodus (1) oder Textmodus (0).
MONO Grafik-/Textmodus monochrom (1) oder farbig (0).
9EQ8 ASCII-Codes c0H..dfH in 9bit-Textmodi (720x???):
Normalerweise basieren 9bit-Textmodi, wie z.B. Mode 03H, lediglich auf 8bit breiten Zeichendefinitionen. Um den Kontrast zu erhöhen, werden die Zeichen dabei so auf dem Bildschirm plaziert, daß das erste Pixel jeder Zeichenmatrixzeile auf eine 0MOD9 Boundary fällt.
Auf diese Weise erhält man automatisch ein horizontales Leerpixel zwischen jeweils zwei nebeneinander liegenden Zeichen, ohne daß die Bits definiert sein müssen (das neunte Bit hätte ein weiteres Byte pro Zeichenmatrixzeile erforderlich gemacht, was zwangsläufig den Umfang jedes Zeichensatzes verdoppelt hätte).
Nun sind diese Leerpixel zwar vorteilhaft bei der Darstellung von Buchstaben, Ziffern und Satzzeichen, nicht jedoch, wenn man z.B. eine durchgehende horizontale ASCII-Linie zeichnen möchte.
Aus diesem Grund besteht für die Ausgabe der ASCII-Codes c0H..dfH die Möglichkeit, das achte Bit jeder Zeichenzeilendefinition dop- pelt ausgeben zu lassen (Bit 9 = Bit 8), indem man 9EQ8 = 1 setzt.
HINWEIS: Für die ASCII-Codes 00H..bfH und e0H..ffH wird generell ein Leerpixel als neuntes Bit erzeugt. Auf diese Codes hat das 9EQ8-Bit absolut keinen Einfluss... ;)
FLSH Blinken (1) oder hohe Intensität (0) bei gesetztem Attribut-Bit 7.
HINWEIS: Yep, durch Löschen des FLSH-Bits kommt man in 16-farbigen Textmodi auch in den Luxus von 16 Hintergrundfarben... :)
HINWEIS: In manchen VGA-Dokumentationen wird fälschlicherweise be- hauptet, daß über dieses Bit der Cursor im Textmodus ein- bzw. ausgeschaltet wird.
USCR Dieses Bit entscheidet darüber, ob sich horizontales und/oder ver- tikales Scrolling nur auf Bildschirmbereiche oberhalb (1) der ak- tuellen Splitline (siehe Line Compare Register, CRTC.LCR, 3d4.18H), oder sowohl oberhalb, als auch unterhalb auswirken soll (0).
Mehr zur Steuerung horizontalen/vertikalen Scrollings ist bei den Registern Horizontal Pixel Panning (ATC.HPPR, 3c0.13H), Preset Row Scan (CRTC.PRSR, 3d4.08H), sowie Linear Start Address High und Low (CRTC.LSAHR, 3d4.0cH bzw. CRTC.LSALR, 3d4.0dH) zu finden.
8BPP Das 8BPP-Bit zeigt an, ob ein Videomodus mit 256 (1), oder weniger Farben (0) aktiv ist.
Damit wird gleichzeitig bestimmt, ob DAC_IDX Angaben im Paletten- bzw. Overscan Color Register (ATC.PALn, 3c0.00H..0fH bzw. ATC.OCR, 3c0.11H) 8bit breit sind (1), d.h. ihren korrespondierenden DAC- Registern entsprechen, oder ob die Indexbildung in Abhängigkeit des nachstehend beschriebenen CS45-Bits zu geschehen hat (0).
CS45 Color Select of Palette Bits 4..5.
Nur Videomodi mit 16 oder weniger Farben:
Dieses Bit bestimmt, welche Bits der Komponenten ATC.PALn.DAC_IDX (3c0.00H..0fH) bzw. ATC.OCR.DAC_IDX (3c0.11H) mit den ATC.CSR.PBnn- Bits (3c0.14H[0..1/2..3]) in welcher Form zu kombinieren sind, um einen endgültigen Farbindex für ein Palettenregister bzw. das Over- scan Color Register zu bilden (zur näheren Beschreibung der Index- bildung siehe ATC.PALn.DAC_IDX).
ATC.OCR, Overscan Color Register (3c0.11H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6|5 4|3 2 1 0| | | | | | | |DAC_IDX| +---+---+-------+
DAC_IDX Farbindex bzw. Teilkomponente zur Bildung eines in die DAC-Ta- belle zeigenden Farbindexes, der die darzustellende Rahmenfarbe (Farbe für den Overscan Frame) festlegt.
Die Indexbildung erfolgt auf die gleiche Weise, wie sie unter ATC.PALn.DAC_IDX (3c0.00H..0fH) beschrieben steht.
HINWEIS: Dem Overscan kann nur in 80-Zeichen Textmodi und allen Grafikmodi außer 04H, 05H und 0dH eine Farbe zugewiesen werden.
HINWEIS: Die Overscan Farbe bezieht sich normalerweise, d.h. bei sichtbarer Display Area, nur auf den Overscan Frame.
Erfolgt allerdings eine ATC-Registerwahl mit gelöschtem PRMD-Bit (Bit 5), wird die Display Area ebenfalls in Overscan Farbe dargestellt, egal welches ATC-Register dabei auch immer gewählt wurde. In einigen VGA-Dokumen- tationen wird das leider nicht erwähnt.
ATC.CPER, Color Plane Enable Register (3c0.12H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5 4|3|2|1|0| | | | | | | | | | | | V | | | | | | | | M |P|P|P|P| | | | U |L|L|L|L| |?|?| X |3|2|1|0| +-+-+---+-+-+-+-+
PLn Bitplane n eingeschaltet (1) oder ausgeschaltet (0).
VMUX Video Test Multiplexer.
Über diese Bits lassen sich in der Regel Funktionstests für jeweils zwei bestimmte Farbleitungen auslösen. Für die meisten Karten gilt:
VMUX Farbbits/Leitungen
00 2 und 0 01 5 und 4 10 3 und 1 11 7 und 6
In den Bits 5 und 4 des Input Status #1 Registers (3daH) erscheinen dann die aktuellen Bitzustände der korrespondierenden Farbleitungen und können zu Diagnosezwecken z.B. mit zuvor gesetzten Farben ver- glichen werden.
HINWEIS: Die Funktionstests arbeiten nur auf 100% kompatiblen VGA- Karten auf die beschriebene Art und Weise.
ATC.HPPR, Horizontal Pixel Panning Register (3c0.13H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3 2 1 0| | | | | | | |?|?|?|?| HPAN | +-+-+-+-+-------+
HPAN Horizontal Panning.
Diese Bits bewirken eine Verschiebung der Fetch Area um eine bestim- mte Anzahl Pixel nach rechts (relativ zum unverschobenen Normalzu- stand).
Allerdings entspricht der gewählte Wert für HPAN nicht automatisch auch immer der Anzahl Pixel, um die die Fetch Area horizontal ver- schoben wird, sondern er wird, je nach aktivem Videomodus, vollkom- men unterschiedlich interpretiert:
+- Anzahl Pixel, um die verschoben wird -+
HPAN 256 Farben 9bit Textmode andere Modes
00 0 1 0 01 - 2 1 02 1 3 2 03 - 4 3 04 2 5 4 05 - 6 5 06 3 7 6 07 - 8 7 08 - 0 -
Da die Zentrierung der Display Area, hier speziell deren horizontale Startposition auf dem Monitor, durch HPAN in keinster Weise berührt wird, hängt der entstehende Bewegungseindruck einzig davon ab, in welcher Art und Weise man HPAN mehrfach hintereinander verändert.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0, 9bit Textmode):
Ist HPAN = 08 (unverschobener Normalzustand) und weist man HPAN nun in kleinen Zeitabständen schrittweise die Werte 00 bis 07 zu, dann entsteht für das menschliche Auge der Eindruck, daß sich das Bild nach LINKS bewegt, was ja auch logisch ist, denn schließlich wird, rein technisch gesehen, die Startposition der Fetch Area (über dem Video-RAM) nach rechts bewegt.
Natürlich kann man über HPAN auch den Eindruck vermitteln, daß sich das Bild nach RECHTS bewegt, allerdings nur unter der Voraussetzung, daß bereits eine Verschiebung vorliegt, d.h. HPAN bereits einen Wert ungleich 08 hat.
Sagen wir, ausgänglich ist HPAN bereits 07 (es liegt bereits eine Verschiebung um 8 Pixel nach rechts vor), setzt man HPAN in kleinen Zeitabständen nacheinander auf 06, 05, 04, 03, 02, 01, 08 (!), und schon entsteht der Eindruck einer Bildbewegung nach RECHTS.
Spätestens jetzt dürfte wohl nun auch der Grund für die Bedingung HPAN <> 08 klar geworden sein. Würden wir einfach von 08 nach 00 de- krementieren, würden wir beim Übergang HPAN = 08 nach HPAN = 07 eine schlagartige optische Verschiebung nach LINKS ernten, obwohl wir ei- gentlich nach RECHTS schieben wollten... ;)
Ok, soweit zur grundsätzlichen Funktion von HPAN. Mit HPAN alleine kann man beim horizontalen Scrolling allerdings keinen Blumentopf gewinnen. Da wir nur die Startposition der Fetch Area (im Video-RAM) änderten, nicht aber deren Breite, wird es zu einem häßlichen stati- schen Wrap kommen, der sich aber durch Umprogrammieren des Offset Registers (siehe CRTC.OFSR.AUPVL, 3d4.13H) beseitigen lässt. Deswei- teren wird das Scrolling ohne eine entsprechende Synchronisation, wie beispielsweise mit dem VRetrace (Input Status #1 Register, 3daH) wohl nicht gerade einen weichen (smoothen) Eindruck vermitteln... &)
Weitere Register, die bei der Umsetzung von horizontalem Scrolling oft hilfreich sind, sind Preset Row Scan, sowie Linear Start Address High und Low (CRTC.PRSR, 3d4.08H; CRTC.LSAHR/LSALR, 3d4.0cH/0dH).
ATC.CSR, Color Select Register (3c0.14H) [RW] +++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3 2|1 0| | | | | | | | | | | | | P | P | | | | | | B | B | | | | | | 6 | 4 | |?|?|?|?| 7 | 5 | +-+-+-+-+---+---+
PB45 Palette Bits 4..5.
nur Videomodi mit 16 oder weniger Farben:
Diese Bits haben nur eine Funktion, wenn ATC.MCR.CS45 (3c0.10H[7]) gesetzt ist. In diesem Fall dienen die PB45-Bits als Ersatz für die Bits 4..5 von ATC.PALn.DAC_IDX (3c0.00H..0fH) bzw. ATC.OCR.DAC_IDX (3c0.11H).
Zusammen mit dem PB67-Bits bestimmen die PB45-Bits dann eine 0MOD16 Basisadresse innerhalb der DAC-Register. Der jeweilige DAC_IDX be- zieht sich immer relativ zu dieser Basis:
DAC-Basis DAC-Basis
+-+-+-+-+ 0000 0 1000 128 |3 2|1 0| 0001 16 1001 144 | | | 0010 32 1010 160 | P | P | 0011 48 1011 176 | B | B | 0100 64 1100 192 | 6 | 4 | 0101 80 1101 208 | 7 | 5 | 0110 96 1110 224 +-+-+-+-+ 0111 112 1111 240
PB67 Palette Bits 6..7:
Nur Videomodi mit 16 oder weniger Farben:
Bei gesetztem ATC.MCR.CS45-Bit (3c0.10H[7]) wirken die PB67-Bits im Zusammenhang mit den PB45-Bits, wie bereits vorstehend beschrieben.
Ist CS45 dagegen gelöscht, wirken die PB67-Bits als 0MOD64 Basis- adresse innerhalb der DAC-Register (der DAC_IDX wirkt auch hier re- lativ zur DAC-Basis). Die vorstehenden PB45-Bits haben in diesem Fall keine Bedeutung (die Bits 4..5 werden dann DAC_IDX entnommen).
PB67 DAC-Basis
00 0 01 64 10 128 11 192
CRTC, Cathode Ray Tube Controller ---------------------------------
+--------------------------------------------------------------------------+ | WARNUNG!!! | | | | Die unsachgemäße Programmierung verschiedener der CRTC-Register kann dem | | an die Grafikkarte angeschlossenen Monitor BLEIBENDE SCHÄDEN zufügen! | | | | Das höchste Risiko stellen hier die Register 00H bis 07H dar. Über diese | | Register lässt sich das Darstellungstiming derart extrem beeinflussen, | | daß durchaus die Möglichkeit eines Monitor-Defekts besteht. Deutlich un- | | strichen wird der Gefahrencharakter übrigens dadurch, daß das BIOS beim | | Setzen eines jeden Videomodus diese Register temporär mit einem Schreib- | | schutz (Setzen des CRTC.VRER.R07WD-Bits, 3d4.11H[7]) versieht. | | | | Den Experementierfreudigen möchte ich DRINGENDST empfehlen, bereits bei | | ersten Anzeichen abnormen Monitorverhaltens (quietschende oder pfeifende | | Geräuschentwicklung, ständig zusammenfallender, oder extrem flackernder | | Bildaufbau, usw.) den Monitor SOFORT abzuschalten. | +--------------------------------------------------------------------------+
Über welche Ports der CRTC zu programmieren ist, hängt davon ab, ob die VGA gerade im farbigen, oder monochromen Modus betrieben wird. Feststellen lässt sich das durch Auslesen von Port 3ccH (genauer MORR.CEE, 3ccH[0], Miscel- laneous Output Read Register). Ist MORR.CEE gesetzt (Farbe), wird der CRTC über die Ports 3d4H/3d5H, anderenfalls (Mono) über die Ports 3b4H/3b5H ange- sprochen. Im folgenden wird der Monochrommodus außer acht gelassen.
Um ein CRTC-Register auszulesen, muß dem CRTC über den Index Port (3d4H) zu- erst einmal die gewünschte Registernummer mitgeteilt werden. Danach lässt sich das adressierte Register über den CRTC Data Port (3d5H) auslesen.
Das Beschreiben ließe sich zwar ebenfalls problemlos nach dem vorstehenden Schema abwickeln (gewünschte Registernummer nach 3d4H, gewünschten Wert für das gewählte Register nach 3d5H schreiben), in der Praxis wird man aber wohl meist nicht auf den 16bit-Vorteil des Indexports (3d4H) verzichten wollen.
Port 3d4H verarbeitet nämlich nicht nur Bytezugriffe, sondern man kann ihm auch (zumindest bei Schreibzugriffen) Register und Wert direkt in einem Word mitteilen. Bei einem solchem 16bit-Write wird die gewünschte Registernummer im Lo-, und der gewünschte Wert im Hi-Byte des Words erwartet.
HINWEIS: Einige ganz wenige (uralte) VGA-Karten streiken bei 16bit-Writes.
Ich persönlich gehe grundsätzlich davon aus, daß eine VGA-Karte der heutigen Tage 16bit-Writes beherrscht, und messe den paar unfähigen Karten (und in Deutschland kaum anzufindenden) kein Gewicht bei. ;)
Ok, bevor wir zu den CRTC-Registern im Detail kommen, vorab schon mal eine Zusammenfassung aller Bitpositionen von Timingdefinitionen. Speziell bei den über mehrere Register verteilten Definitionen erspart einem das die unnötige Querleserei, wenn die Funktion der Timingdefinition bereits bekannt ist:
Horizontal Total 0..7 in 00H[0..7] Horizontal Display End 0..7 in 01H[0..7] Horizontal Blanking Start 0..7 in 02H[0..7] Horizontal Retrace Start 0..7 in 04H[0..7] Horizontal Retrace End 0..4 in 05H[0..4] Horizontal Blanking End 0..5 in 03H[0..4], 05H[7]
Vertical Total 0..9 in 06H[0..7], 07H[0], 07H[5] Vertical Display End 0..9 in 12H[0..7], 07H[1], 07H[6] Vertical Blanking Start 0..9 in 15H[0..7], 07H[3], 09H[5] Vertical Retrace Start 0..9 in 10H[0..7], 07H[2], 07H[7] Vertical Retrace End 0..3 in 11H[0..3] Vertical Blanking End 0..6 in 16H[0..6]
Line Compare 0..9 in 18H[0..7], 07H[4], 09H[6]
So, nun aber zu den CRTC-Registern im Detail:
CRTC, Index Port 3d4H, Registerwahl +++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | REG_IDX | +---------------+
REG_IDX Gewünschte Registernummer zum nachfolgenden Lesen, nachfolgendem 8bit Schreiben oder als Bestandteil eines 16bit-Write (Lo-Byte):
00H HTR, Horizontal Total Register 01H HDER, Horizontal Display End Register 02H HBSR, Horizontal Blanking Start Register 03H HBER, Horizontal Blanking End Register 04H HRSR, Horizontal Retrace Start Register 05H HRER, Horizontal Retrace End Register 06H VTR, Vertical Total Register 07H OVRR, Overflow Register 08H PRSR, Preset Row Scan Register 09H MSLR, Maximum Scan Line Register 0aH CSR, Cursor Start Register 0bH CER, Cursor End Register 0cH LSAHR, Linear Start Address High Register 0dH LSALR, Linear Start Address Low Register 0eH CLHR, Cursor Location High Register 0fH CLLR, Cursor Location Low Register 10H VRSR, Vertical Retrace Start Register 11H VRER, Vertical Retrace End Register 12H VDER, Vertical Display End Register 13H OFSR, Offset Register 14H ULR, Underline Location Register 15H VBSR, Vertical Blanking Start Register 16H VBER, Vertical Blanking End Register 17H MCR, Mode Control Register 18H LCR, Line Compare Register
CRTC.HTR, Horizontal Total Register (3d4.00H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | HT_CCUS | +---------------+
HT_CCUS Horizontal Total CCUs.
Anzahl CCUs (abzüglich 5) pro Scan Line insgesamt. Diese Angabe bestimmt, wieviele CCUs getaktet werden müssen, um eine komplet- te Scan Line, d.h. Display Area plus Horizontal Overscan, Blank- ing und Retrace, zu verarbeiten.
Wurden (HT_CCUS + 5) CCUs dargestellt, oder anders ausgedrückt, erreicht der Character Counter den Wert (HT_CCUS + 5 - 1), wird der Character Counter automatisch wieder auf Null zurückgesetzt, der Line Counter inkrementiert und abschließend Display Enable wieder aktiviert.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: 5fH = 95. Um eine vollständige Scan Line zu verar- beiten, sind in diesem Modus also 100 (95 + 5) CCU-Takte erfor- derlich.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um die- ses Register beschreiben zu können.
CRTC.HDER, Horizontal Display End Register (3d4.01H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | HDE_CCU | +---------------+
HDE_CCU Horizontal Display End CCU.
Wie man diesen Wert interpretiert, ist Geschmackssache. Man kann ihn als Anzahl sichtbarer CCUs (abzüglich 1) pro Scan Line, ge- nausogut aber auch als letzte darzustellende CCU bezeichnen.
Ich persönlich bevorzuge letzteres, weil es imo den Bezug zum Character Counter besser verdeutlicht.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: 4fH = 79. Tatsächlich entspricht das 80 (79 + 1) sichtbaren CCUs, oder anders ausgedrückt, 80 * 9 = 720 sichtba- ren Pixeln pro Scan Line.
Wie dem auch sei, nach Darstellung der HDE_CCU wird auf jeden Fall Display Enable zurückgenommen. Das bedeutet, daß von nun an keine Daten mehr aus der Fetch Area, sondern ab jetzt der Inhalt des Overscan Color Registers (ATC.OCR, 3c0.11H) als Farbquelle dient, da noch kein Blanking Signal anliegt.
Mit Ende der HDE_CUU beginnt somit gleichzeitig also auch die Darstellung des rechten Overscans.
HINWEIS: In manchen VGA-Dokumentationen wird HDE_CCU als Anzahl sichtbarer Zeichen definiert, aber leider fehlt dann in einigen Fällen ein entsprechender Hinweis, daß der Wert bei dieser Interpretationsweise auch um eins vermindert eingetragen werden muß.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um die- ses Register beschreiben zu können.
CRTC.HBSR, Horizontal Blanking Start Register (3d4.02H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | HBS_CCU | +---------------+
HBS_CCU Horizontal Blank Start CCU.
Mit Erreichen der HBS_CCU im Character Counter wird automatisch das HBlank Signal aktiviert.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: 50H = 80. Mit CCU Nummer 80 beginnt in diesem Vi- deomodus also das horizontale Blanking.
Da bereits eine CCU zuvor (siehe CRTC.HDER.HDE_CCU, 3d4.01H) das Display Enable Signal zurückgenommen wurde, und nun ein Blanking Signal anliegt, wird jetzt auch nicht mehr in Overscan Color dargestellt, sondern der Partikelbeschuss gänzlich eingestellt.
Die Folge ist, daß gar keine Farbe mehr erzeugt wird, was sehr wichtig für den einige CCUs später beginnenden HRetrace ist, da dieser sonst sichtbare Spuren auf dem Bildschirm hinterlassen würde.
Desweiteren bewirkt die Einstellung des Beschusses natürlich auch, daß mit CCU-Nummer 80 der rechte Overscan endet.
Bei den Standardwerten von Videomodus 03H ergibt sich somit ein HBS_CCU - CRTC.HDER.HDE_CUU (3d4.01H) = 80 - 79, d.h. ein genau ein CCU breiter rechter Overscan.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um die- ses Register beschreiben zu können.
CRTC.HBER, Horizontal Blanking End Register (3d4.03H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6 5|4 3 2 1 0| | | | | | | S | | |V| K | | |R| W | | |R| H | | |E| T | HBE04 | +-+---+---------+
HBE04 Bits 0..4 (eines 6bit-Wertes) für Horizontal Blanking End. Bit 5 befindet sich in CRTC.HRER.HBE5, 3d4.05H[7].
Sobald die untersten 6bit des Character Counters dem gesplitteten 6bit-Wert entsprechen, wird das HBlank Signal deaktiviert.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: xxx00010 (CRTC.HRER.HBE5 = 1) -> xx100010.
Ähnlich wie bei CRTC.HRER.HRE (3d4.05H[0..4]) handelt es sich auch hier nicht um eine "richtige" CCU-Nummer, sondern um die bei HRE näher beschriebene, eigentümliche CCU-Kodierung.
Um die effektive CCU-Nummer zu erhalten, müssen wir ausgehend von der CCU, mit der der HRetrace endete (97, s. dazu CRTC.HRER.HRE), solange um eins inkrementieren, bis die untersten 6bit des Charac- ter Counters den Wert xx100010 annehmen:
97 0110 0001 98 0110 0010
98 xx10 0010
Wie man sieht, endet das horizontale Blanking in Videomodus 03H also mit CCU-Nummer 98. Daraus ergibt sich, daß das linke Blanking 98 - 97, also genau eine CCU dauert.
Da das HBlank Signal zurückgenommen wird, Display Enable aber noch nicht wieder aktiviert wurde, dient ab CCU-Nummer 98 auch wieder das Overscan Color Register (ATC.OCR, 3c0.11H) als Farbquelle.
Mit Ende des linken Blankings beginnt folglich der linke Overscan.
Der linke Overscan wiederum endet, sobald der Character Counter CCU-Nummer CRTC.HTR.HT_CCUS (3d4.00H) + 5 - 1 = 95 + 5 - 1 = 99 erreicht, was bedeutet, daß auch er nur 99 - 98 = eine CCU dauert.
Bei Beendigung des linken Overscans hat der CRTC eine vollständige Scan Line abgearbeitet. Daraufhin wird der Character Counter wie- der auf Null zurückgesetzt, der Line Counter inkrementiert, sowie Display Enable wieder aktiviert, und es kann mit der nächsten Scan Line fortgefahren werden.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um diese Bits beschreiben zu können.
SKWHT Horizontal Total Skew.
Anzahl CCUs, um die der CRTC Display Enable noch verzögern soll, nachdem CRTC.HTR.HT_CCUS (3d4.00H) + 5 CCUs, d.h. eine komplette Scan Line dargestellt wurde.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: x00xxxxx. Keine Verzögerung. Die Verzögerung stammt noch aus EGA-Zeiten und war häufig in Textmodi erforderlich, damit dem CRTC noch etwas mehr Zeit zum Laden des ASCII-Codes und Farb- attributes, sowie der Zeichendekodierung blieb.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um diese Bits beschreiben zu können.
VRRE Vertical Retrace Register Enable.
Ist dieses Bit gesetzt, werden die CRTC-Register 10H und 11H nicht nur bei Schreib-, sondern auch bei Lesezugriffen als Vertical Re- trace Start und End Register genutzt. Ist es gelöscht, dienen die Register stattdessen zum Auslesen der Lightpen-Position (Low bzw. High).
Dieses Bit dient nur zur Erhaltung der Abwärtskompatibilität zur EGA. Die meisten VGA-Karten wickeln Lightpen-Reads über gänzlich andere Register (oder Ports) ab (so sie Lightpens überhaupt noch unterstützen), daher ist dieses Bit auf VGA-Karten i.d.R. immer gesetzt.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um dieses Bit beschreiben zu können.
CRTC.HRSR, Horizontal Retrace Start Register (3d4.04H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | HRS_CCU | +---------------+
HRS_CCU Horizontal Retrace Start CCU.
Erreicht der Character Counter die HRS_CCU, wird automatisch ein HSync Signal gesendet, um den HRetrace einzuleiten.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: 55H = 85. Mit CCU-Nummer 85 wird also in diesem Videomodus der horizontale Strahlenrücklauf eingeleitet.
Daraus ergibt sich, daß rechts neben dem rechten Overscan noch HRS_CCU - CRTC.HBSR.HBS_CCU (3d4.02H) = 85 - 80, also weitere 5 CCUs für das rechte Blanking getaktet werden, bevor der HRetrace einsetzt.
Da mit CRTC.HDER.HDE_CUU (3d4.01H) Display Enable zurückgenommen wurde und seit CRTC.HBSR.HBS_CUU (3d4.02H) HBlank aktiv ist, er- folgt auch während des horizontalen Strahlenrücklaufes weiterhin kein Partikelbeschuss, d.h. der HRetrace erfolgt "unsichtbar".
HINWEIS: Dem Character Counter ist die Einleitung des HRetraces herzlich egal. Er wird munter weiter inkrementiert.. ;)
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um die- ses Register beschreiben zu können.
CRTC.HRER, Horizontal Retrace End Register (3d4.05H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6 5|4 3 2 1 0| | | | | | | S | | |H| K | | |B| W | | |E| H | | |5| R | HRE | +-+---+---------+
HRE Horizontal Retrace End.
Wie man angesichts einer Datenbreite von nur 5bit (0..31) unschwer erkennen kann, handelt es sich bei HRE nicht um eine "richtige" CCU-Nummer, sondern um eine etwas eigentümliche CCU-Kodierung.
Sobald die untersten 5bit des Character Counters das nächste Mal den 5bit von HRE entsprechen, wird das HSync Signal wieder zurück- genommen und damit der horizontale Strahlenrücklauf beendet.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: xxx00001.
Da der Character Counter mit Beginn des HRetrace nicht auf Null zurückgesetzt, sondern fröhlich weiter inkrementiert wurde, müssen wir, ausgehend von CCU-Nummer 85, also der CCU, mit deren Ende der HRetrace begann (siehe dazu CRTC.HRSR.HRS_CCU, 3d4.04H), solange um eins inkrementieren, bis die untersten 5bit den Wert xxx00001 annehmen, um aus dem kodierten HRE die "richtige" CCU-Nummer zu gewinnen:
85 0101 0101 86 0101 0110 : 96 0110 0000 97 0110 0001
97 xxx0 0001
Der HRetrace endet folglich nicht, wie man bei einem HRE-Wert von xxx00001 schnell meinen könnte, mit CCU-Nummer 1, sondern mit der CCU-Nummer 97 im Character Counter.
Damit ergibt sich für den HRetrace eine Dauer von (97 - HRS_CCU) = 97 - 85, also 12 CCUs.
Mit CCU-Nummer 97 beginnt außerdem sozusagen die linke Blanking- Phase (HBlank liegt immer noch an, Display Enable ist inaktiv), es wird also immer noch keine Farbe dargestellt.
HINWEIS: Vorsicht! Die Mindestdauer für den horizontalen Strahlen- rücklauf ist durch Kenndaten des Monitors fest vorgegeben und sollte imo keinesfalls unterschritten werden.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um diese Bits beschreiben zu können.
SKWHR Horizontal Retrace Skew.
Anzahl CCUs, die der CRTC das HSync-Signal (und damit den HRetrace s. dazu auch voranstehendes Feld HRE) verzögern soll.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: x00xxxxx. Keine Verzögerung. Die Verzögerung stammt noch aus EGA-Zeiten und war häufig in Textmodi erforderlich, damit dem CRTC noch etwas mehr Zeit zum Laden des ASCII-Codes und Farb- attributes, sowie der Zeichendekodierung blieb.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um diese Bits beschreiben zu können.
HBE5 Bit 5 für Horizontal Blank End (siehe CRTC.HBER.HBE04, 3d4.03H).
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um dieses Bit beschreiben zu können.
CRTC.VTR, Vertical Total Register (3d4.06H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | VT07 | +---------------+
VT07 Bits 0..7 (eines 10bit-Wertes) für Vertical Total. Bit 8 liegt in CRTC.OVRR.VT8, 3d4.07H[0], Bit 9 in CRTC.OVRR.VT9, 3d4.07H[5].
Anzahl Scan Lines (abzüglich 2) pro Bild insgesamt. Der gesplittete 10bit Wert gibt an, wieviele Scan Lines vom CRTC abzuarbeiten sind, um ein komplettes Bild (Display Area plus Vertical Overscan, Blank- ing und Retrace) zu erzeugen.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: bfH = 10111111 (VT8 = 1, VT9 = 0) -> 0110111111 = 447
In diesem Videomodus müssen für jedes komplette Bild insgesamt also 447 + 2 = 449 Scan Lines getaktet werden.
HINWEIS: CRTC.VRER.R07WD (3d4.11H[7]) muß gelöscht sein, um dieses Register beschreiben zu können.
HINWEIS: Die Einheit für Vertical Total hängt davon ab, ob Double Scan aktiv ist, oder nicht (s. CRTC.MSLR.DBLS, 3d4.09H[7], CRTC.MSLR.SLPR, 3d4.09H[4..0], CRTC.MCR.LCB2, 3d4.17H[2]).
CRTC.OVRR, Overflow Register (3d4.07H) [RW] +++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | |V|V| |L|V|V|V| | |R|D|V|C|B|R|D|V| |S|E|T|V|S|S|E|T| |9|9|9|8|8|8|8|8| +-+-+-+-+-+-+-+-+
VT8 Bit 8 für Vertical Total (siehe CRTC.VTR.VT07, 3d4.06H) VDE8 Bit 8 für Vertical Display End (siehe CRTC.VDER.VDE07, 3d4.12H) VRS8 Bit 8 für Vertical Retrace Start (siehe CRTC.VRSR.VRS07, 3d4.10H) VBS8 Bit 8 für Vertical Blanking Start (siehe CRTC.VBSR.VBS07, 3d4.15H) LCV8 Bit 8 für Line Compare (siehe CRTC.LCR.LCV07, 3d4.18H) VT9 Bit 9 für Vertical Total (siehe CRTC.VTR.VT07, 3d4.06H) VDE9 Bit 9 für Vertical Display End (siehe CRTC.VDER.VDE07, 3d4.12H) VRS9 Bit 9 für Vertical Retrace Start (siehe CRTC.VRSR.VRS07, 3d4.10H)
HINWEIS: Alle Bits, ausgenommen LCV8, lassen sich nur dann beschreiben, wenn das CRTC.VRER.R07WD-Bit (3d4.11H[7]) gelöscht ist.
CRTC.PRSR, Preset Row Scan Register (3d4.08H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6 5|4 3 2 1 0| | | | | | | H | | | | S | | | | K | | |?| P | VPAN | +-+---+---------+
VPAN Vertical Panning.
Diese Bits bewirken eine Verschiebung der Fetch Area (im Video-RAM) um die angegebene Anzahl Scan Lines nach unten (relativ zum unver- schobenen Normalzustand).
Eine schrittweise Erhöhung von VPAN erweckt folglich den Eindruck, daß sich das Bild nach OBEN bewegt.
Um ein Bewegung nach UNTEN zu erreichen, ist dementsprechend eine schrittweise Verminderung von VPAN (bei einer Ausgangsbasis von VPAN <> 00) erforderlich.
HINWEIS: Dieses Feld beeinflusst den Wert der effektiven Display Start Address, welche vom CRTC jeweils zum VRetrace-Beginn gelatched wird. Um Flackern zu vermeiden, ist es also rat- sam, VPAN erst kurz VOR (und nicht WÄHREND) eines VRetrace zu verändern.
HINWEIS: Ähnlich wie beim horizontalen Scrolling (s. ATC.HPPR.HPAN, 3c0.13H) wird auch durch VPAN nur die Startposition der Fetch Area verändert (hier natürlich die vertikale, nicht die horizontale). Die Höhe der Fetch Area wird durch VPAN nicht beeinflusst.
HINWEIS: Manche VGA-Dokus behaupten fälschlicherweise, daß VPAN in allen Grafikmodi gegenläufig zu Textmodi wirkt, also daß der unverschobene Normalzustand in Grafikmodi nicht durch VPAN = 0, sondern durch VPAN = 31 definiert sei.
Davon das dem nicht so ist, kann man sich leicht selbst überzeugen (z.B. im Videomodus 13H, 320x200x256)... ;)
HSKP Horizontal Skip.
Anzahl CCUs (0..3), um die die horizontale Startposition der Fetch Area nach rechts verschoben werden soll.
Je nach Wert des TS.CMR.CCU8-Bits (3c4.01H[0]) lässt sich über HSKP eine Bildbewegung um jeweils 8 bzw. 9 Pixel (CCU8 = 1 bzw. CCU8 = 0) auf einmal nach LINKS (bei HSKP-Inkrementierung), bzw. nach RECHTS (bei HSKP-Dekrementierung, Ausgangsbasis HSKP <> 0) erreichen.
HINWEIS: Genau wie durch ATC.HPPR.HPAN (3c0.13H) wird durch HSKP nur die Startposition der Fetch Area verändert, nicht aber der- en Breite. Folglich wird es auch hier zum bei HPAN bereits beschriebenen statischen Wrap kommen, sofern man das Offset Register (CRTC.OFSR.AUPVL, 3d4.13H) nicht in entsprechender Weise anpasst.
CRTC.MSLR, Maximum Scan Line Register (3d4.09H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4 3 2 1 0| | | | | | |D|L|V| | |B|C|B| | |L|V|S| | |S|9|9| SLPR | +-+-+-+---------+
SLPR Scan Lines per Row (0..31). Anzahl Scan Lines (abzüglich 1), aus denen jede SLCU (Scan Line Clock Unit) besteht.
In Textmodi entspricht eine SLCU normalerweise (soweit nicht mani- puliert) der Standardhöhe des Zeichensatzes in Rasterzeilen. Der CRTC "weiß" so, wieviele Bytes pro ASCII-Code aus der Zeichensatz- definition gelesen und dargestellt werden müssen.
In Grafikmodi hat der CRTC beim Zeilenaufbau bekanntlich natürlich keine ASCII-Codes mehr zu dekodieren, demzufolge hat SLPR hier auch eine zwar ähnliche, aber funktionell abweichende Auswirkung. Hier gibt SLPR an, wie oft der Inhalt jeder Fetch Area Zeile darzustel- len ist (0: einfach, 1: doppelt, 2: dreifach, usw.).
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: xxx01111 = 15. In diesem Videomodus besteht eine SLCU folglich aus 15 + 1 = 16 Scan Lines, d.h. es werden pro ASCII-Code der Fetch Area die entsprechenden 16 Bytes aus der Zeichensatzdefi- nition (im ROM oder RAM) ausgelesen und dargestellt.
HINWEIS: In Grafikmodi, die im Double Scan Mode (Zeilenverdopplung) betrieben werden, benutzen viele VGA-Clones SLPR (manche auch CRTC.MCR.LCB2, 3d4.17H[2]) zur Auslösung der Verdop- plung, obwohl dazu eigentlich das weiter unten beschrie- bene DBLS-Bit des Maximum Scan Line Registers dienen soll.
In CGA-Modi trifft man desöfteren sogar auf scheinbare Double-Double Scans ;), d.h. sowohl SLPR = 00001, als auch DBLS = 1, obwohl eigentlich beide jeweils Exklusivstatus besitzen sollten.. &)
VBS9 Bit 9 für Vertical Blanking Start (siehe CRTC.VBSR.VBS07, 3d4.15H)
LCV9 Bit 9 für Line Compare (siehe CRTC.LCR.LCV07, 3d4.18H)
DBLS Double Scan.
Durch Setzen dieses Bits wird der CRTC veranlasst, jede Scan Line doppelt auszugeben. Das Bit wurde mit der VGA eingeführt, um auch die 200 Zeilen-Modi in VGA-Standardauflösung (400 Zeilen) anzeigen zu können, ohne größere Anforderungen an die Synchronisationsfähig- keiten der damaligen Monitore stellen zu müssen.
HINWEIS: In vielen Clones wird statt DBLS häufig das SLPR-Feld des gleichen Registers (oder auch CRTC.MCR.LCB2, 3d4.17H[2]) ¯mißbraucht®, um Double Scan Modes zu aktivieren... ;)
CRTC.CSR, Cursor Start Register (3d4.0aH) [RW] ++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4 3 2 1 0| | | | | | | | |C| | | | |O| | | | |F| | |?|?|F| CTOP | +-+-+-+---------+
CTOP Cursor Top.
Rasterzeile (0..31) innerhalb der Zeichenmatrix, ab der der CRTC mit der Darstellung des Cursors beginnen soll (vorausgesetzt natür- lich COFF = 0).
HINWEIS: Ist CTOP größer als die Höhe des momentanen Zeichensatzes (siehe dazu auch CRTC.MSLR.SLPR, 3d4.09H[0..4]), wird von den meisten VGA-Karten i.d.R. gar kein Cursor mehr darge- stellt (auch wenn COFF = 0 ist).
Gleiches gilt, wenn die Startrasterzeile des Cursor größer als die Endrasterzeile (s. CRTC.CER.CBOT, 3d4.0bH[0..4]) gewählt wird.
COFF Cursor aus (1) oder an (0).
CRTC.CER, Cursor End Register (3d4.0bH) [RW] ++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6 5|4 3 2 1 0| | | | | | | S | | | | K | | | | W | | |?| C | CBOT | +-+---+---------+
CBOT Cursor Bottom.
Rasterzeile (0..31) innerhalb der Zeichenmatrix, mit der die Dar- stellung des Cursors enden soll (vorausgesetzt natürlich COFF = 0).
HINWEIS: Ist CBOT größer als die Höhe des momentanen Zeichensatzes (siehe dazu auch CRTC.MSLR.SLPR, 3d4.09H[0..4]), wird von den meisten VGA-Karten i.d.R. gar kein Cursor mehr darge- stellt (auch wenn COFF = 0 ist).
Gleiches gilt, wenn die Startrasterzeile (s. CRTC.CSR.CTOP 3d4.0aH[0..4] des Cursors größer als CBOT definiert wird.
SKWC Cursor Skew.
Anzahl CCUs (0..3), um die die Darstellung des Cursors verzögert, d.h. weiter nach rechts verschoben werden soll.
Auf VGAs ist SKWC im Normalfall immer x00xxxxx, d.h. der Cursor er- scheint unverschoben auf seiner aktuellen horizontalen Ordinate.
HINWEIS: In manchen VGA-Dokus wird dieses Feld nicht erwähnt, ob- wohl es meiner Erinnerung nach bereits in der originalen IBM VGA-Doku definiert war (btw: hat die noch einer?).
HINWEIS: Soweit ich informiert bin, soll irgendeine VGA-Doku (evtl. PC Underground??? Kann das bitte mal einer checken?) be- haupten, daß sich über SKWC angeblich der Cursor auch ganz außen (links u. rechts) positionieren ließe.
Wenn ich diese Aussage richtig deute, müsste damit ein Po- sitionieren des Cursors im Overscan Frame gemeint sein.
Daß wäre mir völlig neu und ich wage mal zu behaupten, daß es sich dabei um einen Dokumentationsfehler handelt. Mei- nes Wissens lassen sich bei entsprechender Spaltenposition über SKWC-Manipulationen allenfalls horizontale Doppeldar- stellungen des Cursors erzeugen, aber Cursor im Overframe?
CRTC.LSAHR, Linear Start Address High Register (3d4.0cH) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | LSA8F | +---------------+
LSA8F Bits 8..15 für Linear Start Address (s. CRTC.LSALR.LSA07, 3d4.0dH)
CRTC.LSALR, Linear Start Address Low Register (3d4.0dH) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | LSA07 | +---------------+
LSA07 Bits 0..7 (eines 16bit-Wertes) für die Linear Start Address. Die Bits 8..15 liegen im voranstehenden Register CRTC.LSAHR (3d4.0cH).
Über diesen gesplitteten 16bit-Wert bestimmt man den Ursprung der linken, oberen Ecke der Fetch Area (über dem Video-RAM), oder an- ders ausgedrückt, den Offset der Fetch Area relativ zum Beginn des Video-Segmentes.
Mit der Linear Start Address läßt sich einiges anfangen. Sie wird z.B. häufig für horizontales/vertikales Scrollen oder zum Umschal- ten auf andere Bildschirmseiten (sogenannte PageFlips, u.a. sehr hilfreich für z.B. Double/Triple Buffering) verwendet.
In welcher Art und Weise der CRTC Wertangaben für eine LSA erwar- tet bzw. sie interpretiert, hängt davon ab, ob es sich beim akti- ven Videomodus um einen Odd/Even-, oder einen Chain4-Modus handelt (siehe dazu auch TS.MMR (3c4.04H) sowie GDC.MR (3ce.05H)).
Im Odd/Even Mode muß der gewünschte (reale) Byte-Offset erst durch 2, im Chain4 Mode erst durch 4 dividiert werden, bevor er in die Linear Start Address Register eingetragen wird.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Beim Videomodus 03H handelt es sich um einen Odd/Even Mode. Möchte man den Fetch Area Start beispielsweise um 25 Textzeilen, also 2 * 80 * 25 = 4000 Bytes (eine Bildschirmseite) nach hinten ver- schieben, wäre ein Wert von 4000 / 2 = 2000 = 07d0H in die Linear Start Address Register einzutragen (3d4.0cH = 07H, 3d4.0dH = d0H).
HINWEIS: Der CRTC liest die Linear Start Address Register mit Aus- lösung des VSync Signals, d.h. mit Beginn des VRetraces aus, um die LSA für den nächsten (!) Bildschirmaufbau zu ermitteln. Folglich hat das Beschreiben der Linear Start Address Register i.d.R. KEINE unmittelbare Auswirkung auf die Bildschirmdarstellung. Werden gleichzeitig beispiels- weise DIREKT reagierende Panning-Register (HPAN/VPAN) ma- nipuliert, sind die Aktionen entsprechend zu synchroni- sieren, will man keine Uglies ernten... 8)
HINWEIS: Beim Lesen/Setzen der Linear Start Address Register sol- lte man sich über die "ungewöhnliche" Reihenfolge der Re- gister (High, Low) bewußt sein.
Als Assemblerprogrammierer denkt man oft im Little Endian Format (Low, High) und beschickt oder interpretiert die Register dann unter Umständen falsch... ;)
CRTC.CLHR, Cursor Location High Register (3d4.0eH) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | CL8F | +---------------+
CL8F Bits 8..15 der Cursor Location (siehe CRTC.CLLR.CL07, 3d4.0fH)
CRTC.CLLR, Cursor Location Low Register (3d4.0fH) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | CL07 | +---------------+
CL07 Bits 0..7 (eines 16bit-Wertes) für Cursor Location, Die Bits 8..15 befinden sich im voranstehenden Register CRTC.CLHR (3d4.0eH).
Der gesplittete 16bit-Wert versteht sich relativ zum Beginn des Video-Segmentes. In welchem Format der CRTC Angaben der Cursor Lo- cation erwartet, hängt von den gleichen Bedingungen ab, wie sie bereits unter CRTC.LSALR.LSA07 (3d4.0dH) beschrieben stehen, also davon, ob ein Odd/Even oder Chain4 Mode aktiv ist.
HINWEIS: Auch beim Lesen/Setzen der Cursor Location Register sol- lte man sich über die "ungewöhnliche" Reihenfolge der Re- gister (High, Low) bewußt sein.
Als Assemblerprogrammierer denkt man oft im Little Endian Format (Low, High) und beschickt oder interpretiert die Register dann eventuell falsch... ;)
HINWEIS: In manchen VGA-Dokumentationen werden falsche Nummern für die Cursor Location Register angegeben. Definitiv richtig sind die Registernummern 0eH und 0fH.
CRTC.VRSR, Vertical Retrace Start Register (3d4.10H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | VRS07 | +---------------+
VRS07 Bits 0..7 (eines 10bit-Werts) für Vertical Retrace Start. Bit 8 in CRTC.OVRR.VRS8 (3d4.07H[2]), Bit 9 in CRTC.OVRR.VRS9 (3d4.07H[7]).
Erreicht der Line Counter die über den gesplitteten 10bit Wert de- finierte Scan Line, sendet der CRTC ein VSync Signal, woraufhin der vertikale Strahlenrücklauf (VRetrace) beginnt.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standard: 9cH = 10011100 (VRS8 = 1, VRS9 = 0) -> 0110011100 = 412
Mit Scan Line Nummer 412 wird somit der VRetrace eingeleitet. Mit dem Strahlenrücklauf endet zwangsläufig auch das untere Blanking. Das untere Blanking dauert folglich VRS - VBS (siehe dazu 3d4.15H) = 412 - 406, also 6 Scan Lines.
Da seit CRTC.VDER (siehe 3d4.12H) Display Enable deaktiv, und seit CRTC.VBSR (siehe 3d4.15H) VBlank aktiv ist, erfolgt während des VRetrace kein Partikelbeschuss. Somit verläuft der vertikale Rück- lauf "unsichtbar".
HINWEIS: Die Einheit für Vertical Retrace Start hängt davon ab, ob Double Scan aktiv ist, oder nicht (siehe CRTC.MSLR.DBLS, 3d4.09H[7], CRTC.MSLR.SLPR, 3d4.09H[4..0], CRTC.MCR.LCB2, 3d4.17H[2]).
HINWEIS: In einigen Dokus findet man widersprüchliche Angaben zur Breite des VRS-Wertes (9bit-Behauptung bei CRTC.VRSR, in CRTC.OVRR.VRS9 (3d4.07H[7]) aber das 10te Bit dann trotz- dem aufgeführt)... :)
HINWEIS: Ähnlich wie beim HRetrace (CRTC.HRSR, 3d4.04H) kein Reset des Character Counters erfolgt, wird auch der Line Coun- ter von einem VRetrace nicht beeinflusst. Er zählt munter weiter... &)
CRTC.VRER, Vertical Retrace End Register (3d4.11H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3 2 1 0| | | | | | | |R|R| | | | |0|C| | | | |7|P|V|V| | |W|S|I|I| | |D|5|D|L| VRE | +-+-+-+-+-------+
VRE Vertical Retrace End.
Ähnlich wie bei Horizontal Retrace End (s. CRTC.HRER.HRE, 3d4.05H) die CCU, ist auch die Scan Line Nummer für das Ende des VRetrace etwas sonderbar kodiert.
Sobald die untersten 4 Bit des Line Counters das nächste Mal VRE entsprechen, nimmt der CRTC das VSync Signal zurück und beendet damit den VRetrace.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: xxxx1110.
Wie bereits bei der Beschreibung von CRTC.VRSR (3d4.10H) erwähnt, wird der Line Counter mit Beginn des VRetrace nicht zurückgesetzt, sondern zählt einfach weiter. Um VRE so zu dekodieren, daß wir die "richtige" Scan Line Nummer erhalten, schnappen wir uns den Wert des Vertical Retrace Starts (412, siehe CRTC.VRSR) und inkremen- tieren solange um eins, bis die untersten 4 Bit den Wert xxxx1110 annehmen:
412 01 1001 1100 413 01 1001 1101 414 01 1001 1110
414 xx xxxx 1110
Der VRetrace endet also mit Scan Line Nummer 414 im Line Counter, und nicht, wie man angesichts xxxx1110 meinen könnte, mit der Scan Line Nummer 14... ;)
Aus den nun bekannten Werten können wir entnehmen, daß der verti- kale Strahlenrücklauf genau solange dauert, wie der CRTC für die Darstellung von 414 - 412, also 2 Scan Lines, benötigen würde.
Gleichzeitig beginnt mit dem Ende des VRetrace natürlich auch das obere Blanking (Display Enable ist noch nicht wieder aktiviert, VBlank liegt aber immer noch an), d.h. es wird weiterhin noch kei- ne Farbe dargestellt.
HINWEIS: Vorsicht! Auch die Mindestdauer für den vertikalen Strah- lenrücklauf wird durch Kenndaten des Monitors fest vorge- geben. Diese Mindestdauer sollte imo nie unterschritten werden.
VIL Vertical Interrupt Latch.
Ist CRTC.VRER.VID (3d4.11H[5], siehe nächste Bitbeschreibung) ge- löscht, wird bei einem VRetrace der Grafikkarte IRQ2 ausgelöst und das IS0R.VIP-Bit (3c2H[7], Input Status #0 Register, Vertical In- terrupt Pending) gesetzt.
Solange das IS0R.VIP-Bit gesetzt ist, werden bei allen nachfolgen- den VRetraces keine weiteren IRQ2-Signale mehr generiert. Folglich sollte eine IRQ2-ISR zum Abschluß dieses Bit wieder löschen, damit weitere VRetrace-Behandlungen möglich sind.
Da Port 3c2H nur beim Lesezugriff als Input Status #0 Register, beim Beschreiben aber als Miscellaneous Output Write Register (mit anderer Bitdefinition) dient, ließe sich das IS0R.VIP-Bit nie lö- schen.
Um dieses Manko zu beheben, dient das hier beschriebene VIL-Bit. Jedes Mal, wenn man das VIL-Bit zunächst löscht und danach wieder setzt, wird durch diese Sequenz das IS0R.VIP-Bit gelöscht und so auch der Weg für weitere IRQ2-Auslösungen wieder frei... &)
VID Vertical Interrupt Disable.
IRQ2 bei VRetrace auslösen (0) oder nicht (1).
Bei gelöschtem VID-Bit löst der nächste VRetrace der Grafikkarte automatisch IRQ2 aus, vorausgesetzt natürlich, die Karte kann das IRQ2-Signal überhaupt erzeugen, und falls ja, die Erzeugung ist auf der Karte auch aktiviert (DIPs/Jumper).
Ist das der Fall, wird während der IRQ2-Auslösung das IS0R.VIP-Bit (Input Status #0 Register, 3c2H[7]) gesetzt, um anzuzeigen, daß ein vertikaler Interrupt anliegt.
HINWEIS: Das VID-Bit wurde mit den EGA-Karten eingeführt, ist aber auf Standard-VGAs i.d.R. funktionslos, da nur die wenigs- ten VGAs mit der Aktivierung des VSyncs (also mit Beginn des VRetrace) standardmäßig ein IRQ2-Signal für die CPU generieren (weil es die originale VGA auch nicht macht).
Die IRQ-Signalgenerierung lässt sich zwar meist über DIP- Switches (yep, good ol' mouse piano ;)) oder Jumper manu- ell ein- oder ausschalten, aber solange ein Programmierer die Funktion der Signalgenerierung nicht softwareseitig steuern kann, ist der Nutzen des VID-Bits für die univer- selle Programmierung halt sehr gering... &)
HINWEIS: Die IRQ2-ISR sollte vor ihren Rücksprung das IS0R.VIP-Bit wieder löschen, da VRetraces bei gesetztem VIP-Bit zwar nach wie vor ablaufen, aber keine weiteren IRQ2-Signale mehr generieren (VRetraces werden also nicht gepuffert).
Gelöscht werden muß das IS0R.VIP-Bit immer wieder (Latch) durch eine Lösch-Setz-Sequenz des vorstehend beschrieben- en CRTC.VRER.VIL-Bits (3d4.11H[4]).
RCPS5 Refresh Cycles per Scanline: fünf (1), oder drei (0).
HINWEIS: Fünf Refresh Cycles sind für 15.75 KHz Displays gedacht.
R07WD Register 0..7 Write Disable.
Bei gesetztem R07WD-Bit können in den CRTC-Registern 00..07H keine Bits beschrieben werden (ausgenommen CRTC.OVRR.LCV8, 3d4.07H[4]).
HINWEIS: Dieses Bit wird vom BIOS aus Sicherheitsgründen bei der Aktivierung eines jeden Videomodus grundsätzlich erst mal gesetzt, da die betroffenen Register erheblichen Einfluss auf das horizontale und vertikale Videotiming haben.
Siehe dazu auch die AUSDRÜCKLICHE WARNUNG zu Beginn des CRTC-Kapitels... >-)
CRTC.VDER, Vertical Display End Register (3d4.12H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | VDE07 | +---------------+
VDE07 Bits 0..7 (eines 10bit-Wertes) für Vertical Display End. Bit 8 in CRTC.OVRR.VDE8 (3d4.07H[1]), Bit 9 in CRTC.OVRR.VDE9 (3d4.07H[6]).
Den gesplitteten 10bit Wert für das Vertical Display End kann man, ähnlich wie bei Horizontal Display End (s. CRTC.HDER, 3d4.01H) auf zweierlei Weise beschreiben: Zum einen als Anzahl der sichtbaren Scan Lines (abzüglich 1), oder zum anderen als Nummer der letzten inhaltlich zu interpretierenden Scan Line im Line Counter (0..).
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standard: 8fH = 10001111 (VDE8 = 1, VDE9 = 0) -> 0110001111 = 399
Die Display Area im Videomodus 03H besteht folglich aus 399 + 1, also 400 Scan Lines (das entspricht bei angewandter 9x16 Zeichen- matrix unseren allseits bekannten 400 / 16 = 25 ASCII-Zeilen).
Aus Sicht des Line Counters ist Scan Line Nummer 399 also die let- zte zu interpretierende Zeile der Fetch Area. Danach wird der CRTC Display Enable zurücknehmen, was natürlich bedeutet, daß von nun an das Overscan Color Register (ATC.OCR, 3c0.11H) als Farbquelle dient.
Mit Ende der Scan Line Nummer 399 beginnt in diesem Modus folglich auch die Darstellung des unteren Overscans.
CRTC.OFSR, Offset Register (3d4.13H) [RW] +++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | AUPVL | +---------------+
AUPVL Address Units per Video Line.
Über AUPVL kann man dem CRTC mitteilen, wieviele Bytes die Distanz von Zeilenbeginn zu Zeilenbeginn in der Fetch Area beträgt. Anders ausgedrückt lässt sich über AUPVL also die "Breite" der Fetch Area (Zeilenlänge im Video-RAM) manipulieren (Vorsicht, Stolperfalle: die Breite der Display Area wird davon nicht berührt).
Die zu verwendende Einheit für AUPVL-Angaben hängt dabei vom akti- ven Videomodus, bzw. genauer, von dessen Adressierungsmodus (Byte, Word, oder DWord Mode) ab. Der Adressierungsmodus wird über zwei bestimmte Bits des CRTCs gesteuert: CRTC.ULR.ADR4 (3d4.14H[6]) und CRTC.MCR.ADR1 (3d4.17H[6]).
CRTC.ULR.ADR4 CRTC.MCR.ADR1 Einheit für AUPVL
0 AND 1 = 2 Byte (Byte Mode) 0 AND 0 = 4 Byte (Word Mode) 1 AND * = 8 Byte (DWord Mode)
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: AUPVL = 28H = 40 (ADR4 = 0, ADR1 = 0 -> Word Mode).
Für den CRTC sind Zeilen der Fetch Area im Videomodus 03H folglich AUPVL * Word Mode = 40 * 4 = 160 Bytes (80 ASCII + 80 Attribute) lang. Eine Änderung von AUPVL, von 40 auf z.B. 41, ist sehr hilf- reich für die Umsetzung horizontalen Scrollings (speziell in Text- modi, siehe dazu auch ATC.HPPR.HPAN, 3c0.13H[0..3]).
Warum? Nun, dazu muß ich etwas weiter ausholen:
Normalerweise ist eine Fetch Area Zeile im Videomodus 03H für den CRTC, wie gerade erwähnt, 160 Bytes lang, wobei 80 dieser Bytes unsere wohlbekannten ASCII-Codes sind. Jeder dieser ASCII-Codes wird dekodiert und in ein 9 Pixel breites Zeichen (Character) um- gewandelt. Jede Fetch Area Zeile bietet dem CRTC somit Daten, an- hand derer sich 80 * 9 = 720 Pixel (0..719) erzeugen lassen.
Da die Display Area in diesem Videomodus normalerweise ebenfalls 720 Pixel (80 CCUs) breit ist, ist bei standardmäßig unverschobe- ner Fetch Area auch kein Problem zu erwarten; der CRTC wird wie gewünscht die Pixel 0..719 darstellen, bevor aus Fetch Area Sicht die jeweils nächste Zeile beginnt.
Aber was passiert, wenn wir nun die horizontale Startposition der Fetch Area ändern, z.B. durch ATC.HPPR.HPAN = 00, also einer Ver- schiebung um ein Pixel nach rechts?
Richtig. Für den CRTC würde jede Zeile der Fetch Area nicht mehr mit Pixel 0, sondern mit Pixel 1 beginnen. Er wird also zunächst Pixel 1..8 des ersten ASCII-Codes, und danach die Pixel 0..8 der nachfolgenden 79 ASCII-Codes darstellen (Pixel 1..719 eben), dann aber, weil AUPVL nach wie vor behauptet, jede Fetch Area Zeile sei 720 Pixel lang, Pixel 0 aus der nächsten (!) Zeile "anhängen".
Oops. Da ist er, der häßliche statische Wrap. ;)
Ergo suggerieren wir dem CRTC einfach breitere Fetch Area Zeilen, indem wir beispielsweise AUPVL auf 41 statt auf 40 setzen. Darauf- hin ist eine Zeile nun nicht mehr 160, sondern 164 Byte (je 82 ASCII-Codes & Attribute) lang, was 82 * 9 = 738 Pixeln entspricht.
Herrscht ansonsten Normalzustand, ist jetzt also genug Platz, um die Pixel 1..720 darzustellen und den Wrap zu vermeiden. Theore- tisch könnten jetzt zwar noch die Pixel 721..737 zur Darstellung genutzt werden, aber da nach 720 Pixeln Display Enable deaktiviert wird, werden auch nur die Pixel 1..720 aus der Fetch Area in der Display Area dargestellt... ;)
Für das Scrollen nach LINKS wäre eine AUPVL-Änderung auf 41 zudem noch insofern nützlich, daß bei noch nicht verschobener Fetch Area (HPAN = 08) "unsichtbar" ein 81stes Zeichen in die Zeile geschrie- ben werden kann, welches dann erst durch schrittweises Beschreiben von HPAN mit 00..07 mehr und mehr sichtbar wird. Kopiert man da- nach die ASCII-Codes der Positionen 1..80 an die Positionen 0..79, setzt im gleichen Zug HPAN = 08, und fügt ein neues 81stes Zeichen ein, lässt sich (in Verbindung mit irgendeinem Synchronisations- medium wie z.B. dem VRetrace) auch im Textmodus ein butterweiches Scrolling realisieren... 8)
CRTC.ULR, Underline Register (3d4.14H) [RW] +++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4 3 2 1 0| | | | | | | |A|M| | | |D|C| | | |R|B| | |?|4|4| UDL | +-+-+-+---------+
UDL Underline. Nur bei Betrieb im Monochrommodus (Ports 3b4/3b5H):
Rasterzeile (0..31) innerhalb der Zeichenmatrix, in der ein Unter- strich angezeigt werden soll. Ist zwar eigentlich zum Unterstrei- chen gedacht, lässt sich mit entsprechenden Werten aber natürlich auch zum Durchstreichen oder Überstreichen ;) "mißbrauchen"...
HINWEIS: Ist UDL größer als die Höhe des momentanen Zeichensatzes (siehe dazu auch CRTC.MSLR.SLPR, 3d4.09H[0..4]), wird von den meisten VGA-Karten i.d.R. kein Unterstrich mehr darge- stellt.
MCB4 Memory Count by 4. Ist dieses Bit gesetzt, wird der interne Memory Address Counter (für die Fetch Area) nur bei jedem vierten Takt- zyklus der Character Clock erhöht, ist es gelöscht, bei jedem ein- zelnen Taktzyklus.
HINWEIS: Ein gesetztes CRTC.MCR.MCB2-Bit (3d4.17H[3], Memory Count by 2) hat eine höhere Priorität, als das hier beschriebene MCB4-Bit. Ist MCB2 = 1, wird MCB4 ignoriert.
ADR4 DWord Mode Addressing ein (1) oder aus (0).
Ist das ADR4-Bit gelöscht, kontrolliert CRTC.MCR.ADR1 (3d4.17H[6]).
Bei gesetztem ADR4-Bit wird das Video-RAM quasi in vier Planes auf- geteilt. Erreicht wird dies dadurch, daß der jeweils aktuelle Wert des internen Memory Address Counters vor dem Zugriff auf die Plane um 2 Bit nach links rotiert wird (wobei die Bits 14..15 in die Po- sitionen 0..1 gelangen):
Memory Address Counter reale Plane Address
0000..3fffH 0MOD4: 0, 4, 8, ... 4000..7fffH 1MOD4: 1, 5, 9, ... 8000..bfffH 2MOD4: 2, 6, 10, ... c000..ffffH 3MOD4: 3, 7, 11, ...
HINWEIS: ADR4 wirkt indirekt auch auf CRTC.OFSR.AUPVL (3d4.13H).
CRTC.VBSR, Vertical Blanking Start Register (3d4.15H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | VBS07 | +---------------+
VBS07 Bits 0..7 (eines 10bit-Wert) für Vertical Blanking Start. Bit 8 in CRTC.OVRR.VBS8 (3d4.07H[3]), Bit 9 in CRTC.MSLR.VBS9 (3d4.09H[5]).
Bei Erreichen der durch diesen gesplitteten 10bit Wert definierten Scan Line im Line Counter aktiviert der CRTC vollautomatisch das VBlank Signal.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standard: 96H = 10010110 (VBS8 = 1, VBS9 = 0) -> 0110010110 = 406
In Videomodus 03H beginnt also mit Scan Line Nummer 406 das ver- tikale Blanking. Da der CRTC bereits einige Scan Lines zuvor (ge- nauer: mit Ende der Scan Line Nummer 399, siehe CRTC.VDER.VDE07, 3d4.12H) Display Enable zurücknahm und nun ein Blanking Signal an- liegt, dient auch nicht mehr das Overscan Color Register als Farb- quelle, sondern es erfolgt die komplette Einstellung des Partikel- beschusses durch die Kathodenstrahlen.
Ähnlich wie das HBlank Signal bewirkt ergo auch das VBlank Signal, daß gar keine Farbe mehr dargestellt wird. Daraus ergibt sich, daß hier auch zwangsläufig der untere Overscan enden muß. Weiter lässt sich schlußfolgern, daß der untere Overscan im Videomodus 03H also eine Höhe von VBS - VDE (siehe 3d4.12H) = 406 - 399 = 7 Scan Lines haben wird.
Der Fakt, daß mit Aktivierung des VBlank Signals eine Einstellung des Partikelbeschusses erfolgt, ist auch hier wieder Grundvoraus- setzung, daß der einige Scan Lines später folgende VRetrace über- haupt ablaufen kann, ohne sichtbare Spuren zu hinterlassen.
HINWEIS: In einigen VGA-Dokus ist bei der Beschreibung dieses Re- gisters lediglich ein Querverweis auf Bit 8 (VBS8) vor- handen und es fehlt ein entsprechender Verweis auf Bit 9.
Dadurch kann man schnell der Fehlannahme unterliegen, daß Vertical Blank Start über einen nur 9bit breiten Wert de- finiert würde. Tatsächlich sind es natürlich 10bit... ;)
HINWEIS: Vereinzelte VGA-Dokus behaupten fälschlicherweise, Bit 9 (VBS9) wäre im Overflow Register (CRTC.OVRR, 3d4.07H) an- gesiedelt. VBS9 befindet sich definitiv im Maximum Scan Line Register (CRTC.MSLR.VBS9, 3d4.09H[5]).
CRTC.VBER, Vertical Blanking End Register (3d4.16H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6 5 4 3 2 1 0| | | | | | VBE | +-+-------------+
VBE Vertical Blanking End.
Sobald die untersten 7 Bit des Line Counters das nächste Mal den untersten 7 Bit von VBE entsprechen, wird das VBlank Signal vom CRTC deaktiviert.
HINWEIS: IBM hat für die ursprüngliche VGA nur die Bits 0..6 zur Aufnahme des VBE-Wertes definiert. Einige VGA-Clones und mehrere SVGAs (in VGA-Modi) halten sich nicht daran und verwenden alle 8 Bits zur Festlegung des VBE-Wertes... &)
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Standardwert: x0111001
Ähnlich wie bei CRTC.VRER.VRE (3d4.11H[0..3]) kommt auch hier wie- der die bei VRE bereits näher beschriebene, sonderbare Scan Line Kodierung zum Einsatz.
Um die tatsächliche Scan Line Nummer zu erhalten, bei deren Errei- chen VBlank zurückgenommen wird, müssen wir ausgehend von der Scan Line, mit der der VRetrace endete (414, siehe dazu CRTC.VRER.VRE), wiederum solange um eins inkrementieren, bis die untersten 7 Bit des Line Counters mit dem VBE-Wert (x0111001) übereinstimmen:
414 01 1001 1110 415 01 1001 1111 : 440 01 1011 1000 441 01 1011 1001
441 xx x011 1001
In Videomodus 03H endet das horizontale Blanking, wie ersichtlich, mit Scan Line Nummer 441, was bedeutet, daß das obere Blanking die Zeit für die Darstellung von 441 - 414 = 27 Scan Lines in Anspruch nimmt.
Da der CRTC mit Scan Line Nummer 441 zwar das VBlank Signal wieder zurücknimmt, Display Enable aber noch nicht wieder aktiviert, wird die Farbe vorerst dem Overscan Color Register (ATC.OCR, 3c0.11H) entnommen, woraus folgt, daß sich an das Ende des oberen Blankings direkt der obere Overscan anschließt... ;)
Der obere Overscan wiederum endet, sobald der Line Counter den de- finierten Vertical Total Wert + 2 - 1 (s. CRTC.VTR.VT07, 3d4.06H) erreicht. Im unserem Beispiel wäre das VT + 2 - 1 = 447 + 2 - 1, also die Scan Line Nummer 448. Damit ergibt sich, daß der obere Overscan 448 - 441, d.h. 7 Scan Lines hoch sein wird.
Damit hat der CRTC einen kompletten Bildzyklus beendet. Folglich ist es an der Zeit ;), den Line Counter wieder auf Null zu setzen, Display Enable wieder zu aktivieren und mit dem Aufbau des nächs- ten Bildes zu beginnen.
CRTC.MCR, Mode Control Register (3d4.17H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | |H|A|R| |M|L|H|C| |V|D|B| |C|C|R|G| |T|R|1| |B|B|C|A| |E|1|5|?|2|2|D|D| +-+-+-+-+-+-+-+-+
CGAD CGA Compatibility Disable.
Durch ein CGAD = 0 und CRTC.MSLR.SLPR (3d4.09H[0..4]) = 1 kann man den CRTC dazu bewegen, den Speicheraufbau von 320x200x4 CGA-Modi CGA-kompatibel (VRAM:0000..1fffH = Daten für gerade Zeilennummern, VRAM:2000..3fffH = Daten für ungerade Zeilennummern) zu emulieren.
Durch Löschen des CGAD-Bits wird vor jedem Zugriff auf die Fetch Area Bit 13 des internen Memory Address Counters durch Bit 0 des internen Line Counters ersetzt.
Dadurch ergeben sich dann auch wie gewünscht mit jedem Zeilentakt wechselweise Fetch Area Adressen zwischen 0000H + n und 2000H + n. Damit der Memory Address Counter beim Wechsel zwischen geraden und ungeraden Zeilennummern nicht bereits wie üblich erhöht wird, ist SLPR = 1 nötig.
HRCD Hercules Compatibility Disable.
Durch CGAD = 0, HRCD = 0 und CRTC.MSLR.SLPR (3d4.09H[0..4]) = 3 lässt sich der Speicheraufbau von 320x200x16 Grafikmodi emulieren.
Das Prinzip ist artverwandt mit dem des CGAD-Bits. Hier werden nun allerdings vier Blöcke (VRAM:0000..1fffH = 0MOD4 Zeilennummern, VRAM:2000..3fffH = 1MOD4 Zeilennummern, VRAM:4000..5fffH = 2MOD4 Zeilennummern, VRAM:6000..7fffH = 3MOD4 Zeilennummern) erzeugt.
Zudem wird hier nicht mehr nur Bit 13 des Memory Address Counters durch Bit 0, sondern auch Bit 14 durch Bit 1 des Line Counters er- setzt. Daraus ergeben sich zwangsläufig die gewünschten Fetch Area Adresswechsel 0000H + n, 2000H + n, 4000H + n, sowie 6000H + n mit jedem Zeilentakt. SLPR = 3 ist auch hier erforderlich, damit der Memory Address Counter zwischen den Wechseln nicht bereits erhöht wird.
LCB2 Line Count by 2.
Durch Setzen von LCB2 wird der interne Line Counter nur noch jede zweite Scan Line erhöht (jede Scan Line wird doppelt ausgegeben).
HINWEIS: LCB2 ist nur eine von drei Möglichkeiten, Double Scan zu aktivieren. Normalerweise geschieht dies über das DBLS-Bit (CRTC.MSLR.DBLS, 3d4.09H[7]). Manche VGA-Clones benutzen stattdessen auch CRTC.MSLR.SLPR (3d4.09H[0..4])... &)
MCB2 Memory Count by 2.
Ist dieses Bit gesetzt, wird der Memory Address Counter (für die Fetch Area) nur bei jedem zweiten Taktzyklus erhöht (CRTC.ULR.MCB4, 3d4.14H[5] wird in diesem Fall ignoriert), ist es gelöscht, hat es keine Funktion und MCB4 bestimmt stattdessen das Geschehen.
RB15 Rotate Bit 15. Nur Word Mode (ADR1 = 0):
Bit 15 (1), oder Bit 13 (0) vor dem Zugriff auf die Fetch Area an Bitposition 0 des aktuellen Memory Adress Counter Wertes rotieren.
HINWEIS: Dieses Bit existiert nur aus Gründen der Abwärtskompatibi- lität zu EGAs, welche insgesamt nur 64 KByte Video-RAM be- saßen. Auf solchen Karten hätte die Rotation von Bit 15 im Word Mode u.U. Video-RAM adressiert, das gar nicht vorhan- den ist... ;)
ADR1 Byte Mode (1) oder Word Mode (0) Addressing.
Bei gesetztem ADR1-Bit (Byte Mode) wird der aktuelle Wert des in- ternen Memory Address Counters unverändert zur Adressierung der Planes benutzt.
Im Word Mode (ADR1 = 0) wird das Video-RAM dagegen in zwei Planes aufgeteilt. Dazu wird normalerweise (RB15 = 1) der jeweils aktuelle Wert des internen Memory Address Counters vor dem Zugriff auf die Plane um 1 Bit nach links rotiert (damit gelangt Bit 15, bzw. das Bit 13 (wenn RB15 = 0) an Position 0):
RB15 = 1 Memory Address Counter reale Plane Address
0000..7fffH Even: 0, 2, 4, ... 8000..ffffH Odd : 1, 3, 5, ...
RB15 = 0 Memory Address Counter reale Plane Address
0000..1fffH Even: 0, 2, 4, ... 2000..3fffH Odd : 1, 3, 5, ...
HINWEIS: Ein gesetztes CRTC.ULR.ADR4-Bit (3d4.14H[5], Address Count by 4) hat eine höhere Priorität, als das hier beschriebene ADR1-Bit. Egal, welchen Wert ADR1 auch immer besitzt, ist ADR4 = 1, wird ADR1 ignoriert.
HINWEIS: ADR1 wirkt indirekt auch auf CRTC.OFSR.AUPVL (3d4.13H).
HVTE Horizontal/Vertical Timing Enable.
Ist dieses Bit gesetzt, wird das normale Timing ausgeführt. Löscht man HVTE, werden solange keine HSync-/VSync-Signale mehr erzeugt, bis man es durch Setzen von HVTE wieder zulässt.
CRTC.LCR, Line Compare Register (3d4.18H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | LCV07 | +---------------+
LCV07 Bits 0..7 (eines 10bit-Wertes) für den Line Compare Value. Bit 8 in CRTC.OVRR.LCV8, 3d4.07H[4], Bit 9 in CRTC.MSLR.LCV9, 3d4.09H[6].
Bei Erreichen der durch diesen gesplitteten 10bit Wert definierten Scan Line im Line Counter wird der interne Memory Address Counter automatisch auf Null zurückgesetzt.
Bei entsprechender Änderung der Linear Start Address (3d4.0cH/0dH) lässt sich im Zusammenhang mit dem Line Compare Value ein horizon- tal geteiler Bildschirm (sogenannter Split Screen) realisieren.
Beispiel (Videomodus 03H, 80x25x16, TS.CMR.CCU8 = 0):
Zum besseren Verständnis von Split Screens müssen wir etwas tiefer in die CRTC-Interna hinabsteigen. Bei Auslösung des VSync Signals, d.h. mit Beginn des VRetrace, lädt der CRTC bereits die neue Linear Start Address (siehe CRTC.LSAHR/LSALR, 3d4.0cH/0dH) für den nächs- ten (!) Bildschirmaufbau. Im Normalfall ist diese gleich Null, d.h. die Fetch Area beginnt mit Offset 0 im Video-Segment b800H. Solange der Line Compare Value nicht kleiner als Vertical Display End ist (siehe dazu CRTC.VDER, 3d4.12H), wird er vom CRTC nicht beachtet.
Im Normalzustand (LCV = 3ffH = 1023) wird der CRTC folgerichtig LCV ignorieren, und brav die 4000 Bytes ab b800:0000H zur Darstellung abgrasen... &)
Ändert man nun aber beispielsweise die Linear Start Address auf den Wert 400, was im Videomodus 03H (O/E Mode) einem Fetch Area Offset von 400 * 2 = 800 = 320H Bytes entspricht, wird der CRTC mit der Darstellung nicht mehr bei b800:0000H, sondern bei b800:0320H be- ginnen, d.h. er wird die ersten 800 / 160 = 5 Textzeilen einfach überspringen und ab der ersten Zeile der Display Area die Daten ab der sechsten Zeile der Fetch Area darstellen.
Bleibt der Line Compare Value bei dieser Aktion unverändert, wird der CRTC nun halt die 4000 Bytes ab b800:0320H ausgeben. Bis jetzt entspricht das ganze nur dem Prinzip, das häufig zum Umschalten auf eine andere Bildschirmseite benutzt wird (naja, nicht so ganz. Wir haben schließlich nur 5 statt 25 Zeilen verschoben).
Ändern wir nun auch noch den LCV z.B. auf die Scan Line #320, wird der CRTC nicht mehr 4000, zunächst nur noch 320 / 16 * 160 = 3200 Bytes (entspricht bei 9x16 Zeichenmatrix 20 Textzeilen) ab der be- besagten Adresse b800:0320H darstellen.
Mit Erreichen von Scan Line #320 im Line Counter wird der Memory Address Counter automatisch auf Null gesetzt, was im Endeffekt na- türlich gleichbedeutend mit b800:0000H ist. Da im Line Counter der Wert für (Vertical Display End + 1) noch nicht erreicht ist (im Vi- deomodus 03H = 399 + 1 = 400), bleibt dem CRTC also nichts anderes übrig ;), als die noch verbleibenden 400 - 320 = 80 Scan Lines, was 80 / 16 = 5 Textzeilen (also zufälligerweise ;) exakt den fehlenden 5 * 160 = 800 Bytes) entspricht, dem Bereich b800:0000H..031fH zu entnehmen.
Da die LSA immer noch unverändert den Wert 400 = 320H besitzt, wird der CRTC beim nächsten VRetrace wieder bei b800:0320H mit dem Auf- bau des oberen Bildschirmteils beginnen, womit sich der ganze Kreis dann auch wieder schließt...
Wem jetzt der Kopf raucht, sollte sich das Ganze nochmal bei einer Tasse Kaffee in Ruhe zu Gemüte führen... ;)
Zusammenfassen lässt sich der ganze Film auch wie folgt:
Durch Setzen von LSA = 400 und LCV = 320 ergibt sich im Videomodus 03H ein Split Screen mit 20 Textzeilen Höhe (b800:0320H..0f9fH) im oberen, und 5 Textzeilen Höhe (b800:0000H..031fH) im unteren Bild- schirmbereich.
DAC, Digital/Analog Converter -----------------------------
Wie der Name es bereits sagt, ist die Hauptaufgabe des DACs das Umwandeln digitaler in analoge Signale. Um die Notwendigkeit einer solchen Umwandlung nachvollziehen zu können, muß man sich über folgendes im Klaren sein:
Bis einschließlich EGA erzeugten Grafikkarten lediglich digitale Signale und sandten diese über vier Leitungen (RGBI, Red, Green, Blue, Intensity) direkt an den Monitor. Digitale Signale sind binärer Natur, d.h. entweder es wird ein Signal gesendet, oder es wird keines gesendet. Welche Stärke ein gesen- detes Signal dabei hat, spielte bis zu EGAs nur eine untergeordnete Rolle.
Mit den VGAs hingegen begannen die Entwickler die Signalstärke auszunutzen, um den maximal 2^4 = 16 möglichen Signalkombinationen zu entkommen, die bei digitaler Signalübertragung ja nur möglich sind. Sie entschieden sich, nicht mehr mit nur zwei (0 und 1), sondern ab sofort mit 64 (0/63, 1/63, .. 63/63) verschiedenen Signalstärken je Farbleitung (RGB) zu arbeiten; analog eben.
Binär lassen sich 64 Abstufungen mit 6 Bit darstellen. Bei drei Grundfarben zu je 6 Bit ergeben sich somit 3 * 6 = 18 Bit, d.h. 2^18 = 262.144 verschie- dene mögliche Kombinationen, was im Gegensatz zu den bis dahin 16 möglichen Kombinationen natürlich als erheblicher Fortschritt zu bezeichnen war.
Zur programmtechnischen Verwaltung wurde der VGA dann kurzerhand die soge- nannte DAC-Tabelle, häufig auch Palette genannt, spendiert. Die DAC-Tabelle besteht dabei aus 256 DAC-Registern (oft auch RGB- o. PEL-Register genannt). Jedes DAC-Register wiederum besteht aus drei Bytes, von denen die untersten 6bit jedes der Bytes die jeweilige Intensität des Rot-, Grün- u. Blauanteils einer Farbe beherbergt:
+----------------- DAC Register n ------------------+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |7|6|5 4 3 2 1 0| |7|6|5 4 3 2 1 0| |7|6|5 4 3 2 1 0| | | | | | | | | | | | | |?|?| Rot | |?|?| Grün | |?|?| Blau | +-+-+-----------+ +-+-+-----------+ +-+-+-----------+
Damit die VGA-Karte schließlich auch entsprechende analoge Signale an den Monitor senden kann, wurde die Karte zusätzlich noch mit drei D/A Wandlern, je einem für jede Grundfarbe (6bit breit), bestückt.
Aus Programmierersicht dient der DAC hauptsächlich der Bestimmung bzw. Er- mittlung von RGB-Werten in bzw. aus der DAC-Tabelle. Anders als bei den an- deren in diesem Topic beschriebenen Hauptbausteinen wird beim DAC nicht mit der üblichen Registeradressierung gearbeitet. Der DAC stellt stattdessen vier verschiedene Ports zur Verfügung, die jeweils feste Aufgaben besitzen:
DAC, Pixel Mask Port, 3c6H [RW] +++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | DISP_AND_MASK | +---------------+
DISP_AND_MASK Bei jedem erneuten Bildschirmaufbau werden die Pixelwerte (Farbindizes) aus dem Video-RAM entnommen und zuerst ein- mal mit DISP_AND_MASK geANDet, bevor die RGB-Werte aus dem zugehörigen DAC-Register gelesen werden.
Standardmäßig ist DISP_AND_MASK = ffH, d.h. normalerweise wird genau das dem Pixelwert (Farbindex) zugehörige DAC- Register benutzt, weil ein ¯Byte AND ffH® bekanntlich nie ein Bit löschen wird.
Durch Setzen anderer Werte in DISP_AND_MASK kann man gan- ze Registerblocks mit einem Schlag einer anderen Farbgrup- pe zuordnen (z.B. 80H..ffH nach 00..7fH bei Mask = 7fH).
DAC, Pixel Read Address Port [W] bzw. DAC Status Port [R], 3c7H +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Port 3c7H hat eine Doppelfunktion. Wird er beschrieben, lässt sich über ihn das Startregister zum nachfolgenden Auslesen eines oder mehrerer DAC- Register über den Pixel Color Value Port (3c9H) festlegen. Wird er dagegen gelesen, kann man ermitteln, ob sich der Pixel Color Value Port (3c9H) nun gerade im Lese-, oder im Schreibmodus befindet.
Pixel Read Address Port [W], 3c7H ---------------------------------
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | START_DAC_REG | +---------------+
START_DAC_REG Festlegung des Start-DAC-Registers (0..255), zum nachfol- genden Auslesen eines oder mehrerer DAC-Register über den Pixel Color Value Port (3c9H).
DAC State Port [R], 3c7H -------------------------
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2|1 0| | | | | | M | | | O | | | D | | reserv. | E | +-----------+---+
MODE Letzter Zugriffsmodus des Pixel Color Value Ports (3c9H):
00 Schreibmodus 01 reserviert 10 reserviert 11 Lesemodus
HINWEIS: Es lässt sich nur der generelle Zugriffsmodus ermitteln. Das sagt nichts darüber aus, ob sich Port 3c9H gerade bei Stufe Rot, Grün oder Blau befindet... &)
Folglich empfiehlt sich vor Lese/Schreibzugriffen grund- sätzlich die Initialisierung mit dem gewünschten Startre- gister (über Port 3c7H bzw. 3c8H).
DAC, Pixel Write Address Port, 3c8H [RW] ++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | START_DAC_REG | +---------------+
START_DAC_REG Festlegung des Start-DAC-Registers (0..255), zum nachfol- genden Beschreiben eines oder mehrerer DAC-Register über den Pixel Color Value Port (3c9H).
DAC, Pixel Color Value Port, 3c9H [RW] ++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6|5 4 3 2 1 0| | | | | | VALUE | +---+-----------+
VALUE Befindet sich der DAC im Schreibmodus (z.B. nach Init mit Startre- gister über den Pixel Write Address Port (3c8H)), erwartet der DAC hier nacheinander drei Bytes: Rot-, Grün- und Blauanteil (in die- ser Reihenfolge) für das aktuelle DAC-Register.
Befindet sich der DAC dagegen in Lesemodus (z.B. nach Init mit ei- nem Startregister über den Pixel Read Address Port (3c7H)), werden stattdessen nacheinander die drei Bytes für den aktuellen Rot-, Grün- und Blauanteil des aktuellen DAC-Registers geliefert.
Unabhängig davon, ob sich der DAC im Lese- oder Schreibmodus be- findet, erhöht er in beiden Fällen selbsttätig die Nummer des nä- chsten zu lesenden/schreibenden DAC-Registers, sobald die Übermit- tlung der drei RGB-Bytes abgeschlossen ist.
Bei entsprechender Initialisierung (z.B. Startregister 0) ermög- licht diese selbsttätige Inkrementierung folglich auch das Ausle- sen bzw. Setzen der ganzen Palette (256 * 3 = 768 Bytes) in einem Rutsch.
HINWEIS: Die meisten DACs reagieren ziemlich empfindlich darauf, wenn ein eingeleiteter Lese- oder Schreibzyklus an Port 3c9H durch das Setzen einer neuen Adresse über Port 3c7H oder 3c8H unterbrochen wird, bevor der Zyklus vollständig abgeschlossen wurde (äußerst sich meist in ungewöhnlichem Verhalten, wie z.B. Lockup oder Palettenänderungen).
Solange man sich eine solche Frikadelle nicht selbst ans Knie nagelt ;), sprich Red-Green-Blue Zyklen selbst nicht komplett abgeschlossen hat, können einem im Prinzip nur Interrupts dazwischenfunken. Ergo: STI - DACOps - CLI. &)
HINWEIS: Obwohl es bei der originalen VGA (IBM) kein Standard war, produzierten bereits einige der ersten VGA-Clone Herstel- ler DACs mit HiColor-Fähigkeiten. Die Bits 6 und 7 von VALUE werden nur von solchen HiColor DACs unterstützt.
Im Normalfall werden sie ignoriert (Schreiben), bzw. sind undefiniert oder ausgeblendet (Lesen).
GDC, Graphics Data Controller -----------------------------
Der GDC regelt den gesamten Datenverkehr, der sich über die vier Latches der VGA abspielt (CPU -> Latches -> Video-RAM bzw. Video-RAM -> Latches -> CPU).
Bei diesen Latches handelt es sich um vier interne 8bit-Register, die eine tragende Rolle bei der Programmierung von Grafikmodi spielen, die mit mehr- eren Bitplanes arbeiten. In diesen Videomodi wird die Farbe eines einzelnen Pixels nicht in einem korrespondierenden Byte im Video-RAM vermerkt (wie es z.B. im linearen Modus 13H (320x200x256) der Fall ist), sondern der Farbwert muß aus mehreren Bits zusammengesetzt werden, die sich an unterschiedlichen Adressen im Video-RAM befinden. Als Beispiel mal eine schematische Abbildung der Farbzusammensetzung für ein Pixel an Koordinate 0, 0 im Videomodus 0dH (320x200x16):
Farbe +-------+ +-+-+-+-+-+-+-+-+ |-|-|-|-|1|1|0|1| +-+-+-+-+-+-+-+-+ | | | | Vierergruppe +----------------------------------+ | | +---------------+ | Pixelfarbbit +-----------------+ | | |/ | ++ | +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |1 (Latch 3) | |1 (Latch 2) | |0 (Latch 1) | |1 (Latch 0) | +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+- |1|?|?|?|?|?|?|?| |1|?|?|?|?|?|?|?| |0|?|?|?|?|?|?|?| |1|?|?|?|?|?|?|?| +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ | Byte 0, Plane 3 | Byte 0, Plane 2 | Byte 0, Plane 1 | Byte 0, Plane 0
Was genau sich zwischen CPU, Latch Registern und Video-RAM abspielt, hängt sehr stark von den Einstellungen der GDC-Register (speziell dem gewählten Read bzw. Write Mode (0..1, bzw. 0..3); siehe GDC.MR, 3ce.05H) ab, daher lässt sich hier keine allgemeingültige Aussage treffen. Sicher ist nur: die Latches haben immer ihre Finger im Spiel... ;)
Programmiert wird der GDC über seinen Indexport (3ceH) und Dataport (3cfH).
Der Zugriff erfolgt wie auch beim CRTC und TS: erst wählt man das gewünschte Register durch Beschreiben des Indexports (3ceH) aus, danach lässt es sich dann über den Dataport (3cfH) auslesen oder beschreiben. 16bit-Writes sind auch beim GDC in der Regel problemlos möglich, aber eine Garantie für diese Fähigkeit gibt es natürlich nicht... &)
Togal. Hier die Register im Einzelnen:
GDC, Index Port 3ceH, Registerwahl ++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | REG_IDX | +---------------+
REG_IDX Gewünschte Registernummer zum nachfolgenden Lesen, nachfolgendem 8bit Schreiben oder als Bestandteil eines 16bit-Write (Lo-Byte):
00H SRR, Set/Reset Register 01H SRER, Set/Reset Enable Register 02H CCR, Color Compare Register 03H FSR, Function Select Register 04H RMSR, Read Map Select Register 05H MR, Mode Register 06H MISC, Miscellaneous Register 07H CDCR, Color Don't Care Register 08H BMR, Bit Mask Register
GDC.SRR, Set/Reset Register (3ce.00H) [RW] ++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |P|P|P|P| | | | | |L|L|L|L| |?|?|?|?|3|2|1|0| +-+-+-+-+-+-+-+-+
PLn Plane 0..3.
nur Write Modes 0 und 3 (siehe GDC.MR.WM, 3ce.05H[0..1]):
Die vier PLn-Bits (das Farbnibble) dienen im Write Mode 3 (generell) und evtl. auch im Write Mode 0 (wenn mindestens ein SEn-Bit im Set/ Reset Enable Register (GDC.SRER.SEn, 3ce.01H[0..3]) gesetzt ist), als Ausgangsfarbbits für ihre jeweilige Plane.
Ob dieses Farbbit unverändert zum Tragen kommt, oder erst noch ver- knüpft wird, hängt allerdings vom Function Select Register (GDC.FSR, 3ce.03H) ab.
GDC.SRER, Set/Reset Enable Register (3ce.01H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |S|S|S|S| | | | | |E|E|E|E| |?|?|?|?|3|2|1|0| +-+-+-+-+-+-+-+-+
SEn Set/Reset Registerbit Enable 0..3.
nur Write Mode 0 (siehe GDC.MR.WM, 3ce.05H[0..1]):
Ist mindestens eines der SEn-Bits gesetzt, dient bei CPU-Schreibzu- griffen auf das Video-RAM der Inhalt bzw. Bitzustand des Set/Reset Registers (GDC.SRR, 3ce.00H[0..3]) als Farbquelle.
Sind alle SEn-Bits gelöscht, dient dagegen der beim CPU-Write ange- gebene Wert (CPUByte) als Ausgangsbasis.
In beiden Fällen hängt der tatsächlich zu schreibende Wert vom Zu- stand des Function Select Registers (GDC.FSR, 3ce.03H) ab, d.h. ob erst noch rotiert (nur bei CPUByte) und/oder verknüpft (bei beiden Farbquelltypen möglich) werden soll, oder nicht.
HINWEIS: Obwohl Write Mode 0 und 3 augenscheinlich sehr artverwandt zu arbeiten scheinen (in beiden Modi wird das GDC.SRR be- nutzt), so hat doch GDC.SRER NUR in Write Mode 0 eine Funk- tion; im Write Mode 3 spielt es KEINE Rolle.
GDC.CCR, Color Compare Register (3ce.02H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |P|P|P|P| | | | | |L|L|L|L| |?|?|?|?|3|2|1|0| +-+-+-+-+-+-+-+-+
PLn Plane 0..3.
nur Read Mode 1 (siehe GDC.MR.RM, 3ce.05H[3]):
Über die vier PLn-Bits lässt sich die Farbe festlegen, nach der bei CPU-Lesezugriffen gesucht werden soll.
HINWEIS: Welche der PLn-Bits überhaupt zugelassen sind, wird über das Color Don't Care Register (GDC.CDCR, 3ce.07H) bestimmt.
GDC.FSR, Function Select Register (3ce.03H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4 3|2 1 0| | | | | | | | | | | B | | |?|?|?| O | ROR | +-+-+-+---+-----+
ROR Rotate Right Count.
nur Write Modes 0 und 3 (siehe GDC.MR.WM, 3ce.05H[0..1]):
Bitanzahl um die das CPUByte nach rechts rotiert werden soll, BEVOR die Funktion aus dem nachstehenden LFS-Feld ausgeführt wird.
HINWEIS: In Write Mode 0 wird nur rotiert, wenn als Farbquelle ein CPU-Byte dient (bei Set/Reset Register als Farbquelle wird ROR ignoriert).
HINWEIS: In einigen VGA-Dokumentationen wird fälschlicherweise be- hauptet, es würde nach links rotiert.
LFS Logical Function Select.
nur Write Modes 0, 2 und 3 (siehe GDC.MR.WM, 3ce.05H[0..1]):
Art der boolschen Verknüpfung zwischen der Farbquelle (CPUByte bzw. Set/Reset Register) und der momentanen LatchColor:
00 keine Verknüpfung 01 AND-Verknüpfung 10 OR-Verknüpfung 11 XOR-Verknüpfung
HINWEIS: Ein CPUByte wird vom GDC in Abhängigkeit des Write Modes unterschiedlich interpretiert. Im Write Mode 2 zählen die Bits 0..3 als Einheit (Farbnibble), im Write Mode 0 dage- gen ist die Einheit Bit (das Bit wird vom GDC automatisch zum Nibble expandiert (1 wird zu 1111, 0 wird zu 0000)).
GDC.RMSR, Read Map Select Register (3ce.04H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1 0| | | | | | | | | | | | | | | | R | |?|?|?|?|?|?| P | +-+-+-+-+-+-+---+
RP Read Plane (0..3).
nur Read Mode 0 (siehe GDC.MR.RM, 3ce.05H[3]):
Nummer (!) der Plane, aus der bei Lesezugriffen auf das Video-RAM Bytes geliefert werden sollen.
HINWEIS: In Odd/Even Modes wählt RP = 00 oder 01 die verketteten Planes 0 UND 1, RP = 10 oder 11 wählt die Planes 2 UND 3.
GDC.MR, Mode Register (3ce.05H) [RW] ++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1 0| | | | | | | | | | |2| | | | | | | |5|S|O| | | | | |6|R|E|R| | W | |?|C|M|E|M|?| M | +-+-+-+-+-+-+---+
WM Write Mode.
00 Write Mode 0 01 Write Mode 1 10 Write Mode 2 11 Write Mode 3
Für die Write Modi 0, 2 und 3 gilt, daß evtl. programmierte Latch- verknüpfungen (siehe GDC.FSR.LFS, 3ce.03H[3..4]) grundsätzlich mit den momentan aktuellen Latch-Inhalten ausgeführt werden.
Vorsicht, Stolperfalle: Der GDC lädt die Latches NICHT eigenständig mit den Pixelwerten, die der gewählten Schreibposition entsprechen, sondern nutzt die Latch-Inhalte des jeweils letzten durchgeführten CPU-Lesezugriffes. Eine direkte Wertzuweisung an die Latch Register ist offiziell nicht möglich.
Write Mode 0: -------------
Write Mode 0 ist einer der beiden komplexen (der andere ist Write Mode 3) Schreibmodi der VGA. Zu seinen Stärken zählt das Umfärben von Pixeln (bis zu 8 gleichzeitig), basierend auf deren momentaner Farbe. Die Arbeitsweise des Write Mode 0 lässt sich über die GDC- Register SRR, SRER, FSR und BMR (3ce.00H, .01H, .03H und .08H), und das Timing Sequencer Register TS.WPMR (3c4.02H) beeinflussen.
Zum reinen Setzen von Pixelfarben (also wenn der ursprüngliche Wert von Pixelfarben nebensächlich ist) sind aus Performancegründen aber eher Write Mode 1 (bei Blockoperationen gleicher Farbe), oder auch Write Mode 2 (bei Einzelpixeln an nichtzusammenhängenden Adressen) zu empfehlen.
Nachstehend die Arbeitsweise von Write Mode 0 als Pseudocode:
if GDC.SRER.SEn = 0 then
; Farbgebung über CPUByte
if GDC.FSR.ROR <> 0 then for i = 0 to GDC.FSR.ROR do ROR(CPUByte) endfor endif
for i = 0 to 7 do if GDC.BMR[i] = 1 then for p = 0 to 3 do case GDC.FSR.LFS of 00: LatchByte[p][i] = CPUByte[i] 01: LatchByte[p][i] = CPUByte[i] AND LatchByte[p][i] 10: LatchByte[p][i] = CPUByte[i] OR LatchByte[p][i] 11: LatchByte[p][i] = CPUByte[i] XOR LatchByte[p][i] endcase endfor endif endfor
else
; Farbgebung über Set/Reset Register (GDC.SRR, 3ce.00H)
for i = 0 to 7 do if GDC.BMR[i] = 1 then for p = 0 to 3 do if GDC.SRER[p] = 1 then case GDC.FSR.LFS of 00: LatchByte[p][i] = GDC.SRR[i] 01: LatchByte[p][i] = GDC.SRR[i] AND LatchByte[p][i] 10: LatchByte[p][i] = GDC.SRR[i] OR LatchByte[p][i] 11: LatchByte[p][i] = GDC.SRR[i] XOR LatchByte[p][i] endcase endif endfor endif endfor
endif
for p = 0 to 3 do if TS.WPMR[i] = 1 then Write LatchByte[p] to Video-RAM endif endfor
Write Mode 1: -------------
Die Stärken von Write Mode 1 sind zum einen Blocktransfers inner- halb des Video-RAMs, und zum anderen Blockfills mit ein- und der- selben Farbe (bzw. Farbmustern).
Write Mode 1 ist der simpelste aller vier Write Modi. Er schreibt einfach nur den momentanen Inhalt der Latches in die Bitplanes, die laut Write Plane Mask Register (TS.WPMR, 3c4.02H) freigegeben sind. Die GDC-Register SRR, SRER, FSR und BMR haben KEINEN Einfluss auf den Write Mode 1.
Write Mode 2: -------------
Bei Write Mode 2 handelt es sich um eine schnellere Variante des Write Mode 0. Die Geschwindigkeitssteigerung resultiert daraus, daß im Write Mode 2 von den GDC-Registern nur noch FSR und BMR (3ce.03H und .08H) zum Tragen kommen. Die Register SRR und SRER (3ce.00H und .01H) spielen (im Gegensatz zu Write Mode 0) im Write Mode 2 KEINE Rolle mehr.
Da das Set/Reset Register wegfällt, dient im Write Mode 2 auch nur noch ausschließlich das CPUByte als Farbquelle. Allerdings wirkt das CPUByte nicht mehr, wie von Write Mode 0 gewöhnt (Bitexpansion) sondern übernimmt praktisch die Rolle des Set/Reset Registers, d.h. die unteren 4 Bit des CPUBytes werden als Einheit (Farbnibble) be- handelt.
Write Mode 3: -------------
Ähnlich wie bei Write Mode 0, liegt auch bei Write Mode 3 die Kon- zentration verstärkt auf Pixelmanipulationen, die auf momentanen Farbwerten basieren.
Im Gegensatz zu Write Mode 0 dient im Write Mode 3 ausschließlich das Set/Reset Register (GDC.SRR, 3ce.00H) als Farbquelle (der In- halt von GDC.SRER spielt dabei KEINE Rolle).
Wie bei Write Mode 0 sind auch im Write Mode 3 das Write Plane Mask Register (TS.WPMR, 3c4.02H) des Timing Sequencers, und wie gehabt auch die GDC-Register FSR und BMR (3ce.03H und .08H) involviert.
Was das BMR betrifft, so gibt es jedoch einen Unterschied: Das beim Schreibzugriff auf das Video-RAM verwendete CPUByte kann sozusagen zum Overriden des BMRs eingesetzt werden. Im Write Mode 3 wird das BMR nämlich erst mit dem CPUByte geANDet, um endgültig zu erfahren, welche Bits der Latches geändert werden dürfen, und welche nicht.
RM Read Mode.
0 Read Mode 0 1 Read Mode 1
Read Mode 0: -------------
Im Read Mode 0 führt ein CPU-Lesezugriff auf das Video-RAM dazu, daß der GDC entweder (wenn TS.MMR.C4E, 3c4.04H[3] = 0) das adres- sierte Byte aus der in GDC.RMSR.RP (3ce.04H[0..1]) definierten Pla- ne, oder (wenn TS.MMR.C4E = 1) aus Plane ¯Leseadresse MOD 4® holt.
In ersten Fall lassen sich also die Farbbits (der Plane n) von bis zu 8 nebeneinanderliegenden Pixeln auf einen Schlag ermitteln. Um die Gesamtfarbe eines jeden Pixels zu ermitteln, reicht das natür- lich NICHT aus, dazu müssen logischerweise alle Planes ausgelesen, und die korrespondierenden Bits der jeweiligen Bytes zusammengebaut werden.
Geht es um die Suche von Pixeln mit bestimmter Farbe, ist aus Per- formancegründen daher auf jeden Fall eher Read Mode 1 zu empfehlen.
Wie dem auch sei, die Stärke von Read Mode 0 sind also Blocktrans- fers: entweder planeweise vom Video-RAM ins CPU-RAM, oder alle Pla- nes gleichzeitig, dieses aber dann nur innerhalb des Video-RAMs.
Read Mode 1: -------------
Die Stärke von Read Mode 1 ist eindeutig das Auffinden von Pixeln, die einen bestimmten Farbwert besitzen.
Dazu definiert man zuerst über das Color Compare Register (GDC.CCR, 3ce.02H) die Farbe, nach der man suchen will, und lädt das GDC.CDCR (3ce.07H) mit dem Wert 0fH.
Führt man nun einen CPU-Lesezugriff auf das Video-RAM durch, lädt der GDC zunächst einmal die der verwendeten Adresse entsprechenden Bytes aus den jeweiligen Planes in seine Latches. Damit stehen also direkt 8 nebeneinanderliegende Pixel zum Farbvergleich bereit (weil ja Bit 0 aus den Latches 0, 1, 2 und 3 die Farbe von Pixel 0, usw., ausmacht).
Dann vergleicht der GDC für jedes Pixel, ob die Farbe mit der im Color Compare Register (GDC.CCR) definierten Farbe übereinstimmt. Ist das der Fall, wird das dem Pixel entsprechende Bit im später zurückgegebenen CPUByte gesetzt, anderenfalls wird es gelöscht.
Verwendet man nun aber im Color Don't Care Register (GDC.CDCR) ei- nen anderen Wert als 0fH, wird für jedes gelöschte Bit im CDCR das korrespondierende Bit im CCR beim Farbvergleich ignoriert, d.h. es ist völlig unerheblich, welchen Wert das Farbbit der entsprechenden Plane im zugehörigen Latchbyte dann hat, für diese Plane gilt der Vergleich grundsätzlich als übereinstimmend erfüllt.
OEE Odd/Even Enable.
Ist dieses Bit gesetzt, arbeitet der GDC im Odd/Even Mode, anderen- falls im linearen Modus.
HINWEIS: GDC.MR.OEE und TS.MMR.OED (3c4.04H[2]) sollten immer nur paarweise gewählt werden (Odd/Even: [OEE = 1 und OED = 0], Linear: [OEE = 0 und OED = 1]), sonst verarbeitet der GDC die Daten anders als der TS. Das kann nicht gutgehen... ;)
HINWEIS: Der Chain4 Mode besitzt höchste Priorität. Ist dieser Mo- dus aktiviert (TS.MMR.C4E = 1, 3ce.04H[3] = 1), werden die Odd/Even Steuerbits OEE und TS.MMR.OED ignoriert.
SRM Shift Register Mode.
Odd/Even Planenummern verschieben (1) oder nicht (0). Das SRM-Bit ist normalerweise immer gelöscht und wird nur für vierfarbige CGA- Modi (04H und 05H) gesetzt.
256C 256 Color Mode aktiv (1) oder nicht (0).
GDC.MISC, Miscellaneous Register (3ce.06H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3 2|1|0| | | | | | | | | | | | | | V |O|G| | | | | | M |E|F| |?|?|?|?| M |E|X| +-+-+-+-+---+-+-+
GFX Grafikmodus (1) oder Textmodus (0).
OEE Odd/Even (1) oder Linear Mode (0).
VMM Video Memory Mapping:
Segment Größe
00 a000H 128 KByte 01 a000H 64 KByte 10 b000H 32 KByte 11 b800H 32 KByte
GDC.CDCR, Color Don't Care Register (3ce.07H) [RW] ++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |C|C|C|C| | | | | |E|E|E|E| |?|?|?|?|3|2|1|0| +-+-+-+-+-+-+-+-+
CEn Care Enable 0..3.
nur Read Mode 1 (siehe GDC.MR.RM, 3ce.05H[3]):
Jedes gesetzte CEn-Bit führt dazu, daß beim Farbvergleich der Latch- Bits mit den GDC.CCR.PLn-Bits (3ce.02H[0..3]) das korrespondierende PLn-Bit exakt mit dem Latchbit übereinstimmen muss, damit das ent- sprechende Bit im CPU-Rückgabebyte gesetzt (als Farbtreffer gewer- tet) wird.
Über die vier CEn-Bits lassen sich also beliebige Planes für Farb- vergleiche ein- (1) oder ausblenden (0).
GDC.BMR, Bit Mask Register (3ce.08H) [RW] +++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | MMASK | +---------------+
MMASK Modify Mask.
Nur Write Modes 0, 2 und 3 (siehe GDC.MR.WM, 3ce.05H[0..1]):
Jedes gelöschte Bit in MMASK bewirkt, daß die korrespondierenden Bits in den vier Latch Registern unverändert in den Bearbeitungs- zyklus des aktiven Write Modes einfliessen.
Jedes gesetzte Bit bewirkt dagegen, daß dessen Bit-Pedanten in den vier Latch Registern erst noch über das Function Select Register (FSR, 03H) vorbehandelt werden dürfen.
HINWEIS: Im Write Mode 3 lässt sich MMASK über das beim Schreibzu- griff auf das Video-RAM verwendete CPUByte übersteuern. In diesem Modus führt der GDC ein ¯MMASK AND CPUByte® aus um die endgültige Maskierung zu gewinnen.
TS, Timing Sequencer --------------------
Der Timing Sequencer ist in der Hauptsache für das Speichermanagment (inkl. Character Maps (Fonts, Zeichensätze)) und den Refresh des Video-RAMs verant- wortlich.
Programmiert wird der TS über seinen Indexport (3c4H) und Dataport (3c5H).
Das Zugriffsschema ist das gleiche wie bei CRTC und GDC: Auswahl des gewün- schten Registers durch Schreiben der Registernummer nach Port 3c4H, Auslesen bzw. Beschreiben des gewählten Registers durch Zugriff auf Port 3c5H. Was 16bit-Writes (Word Outs über Port 3c4H, Registernummer in Lo-, Registerwert im Hi-Byte) betrifft: sie werden zwar vom nahezu jedem TS unterstützt, aber eben nur nahezu (siehe auch CRTC und GDC)... ;)
TS, Index Port 3c4H, Registerwahl +++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | REG_IDX | +---------------+
REG_IDX Gewünschte Registernummer zum nachfolgenden Lesen, nachfolgendem 8bit Schreiben oder als Bestandteil eines 16bit-Write (Lo-Byte):
00H RR, Reset Register 01H CMR, Clocking Mode Register 02H WPMR, Write Plane Mask Register 03H CMSR, Character Map Select Register 04H MMR, Memory Mode Register
TS.RR, Reset Register (3c4.00H) [RW] ++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1 0| | | | | | | | | | | | | | | | R | |?|?|?|?|?|?| T | +-+-+-+-+-+-+---+
RT Reset Type.
00 reserviert 01 Begin Sequencer Reset (sync, ohne Reset des TS.CMSR) 10 Begin Sequencer Reset (async, mit Reset des TS.CMSR (3c4.03H)) 11 End of Sequencer Reset (normaler Betriebszustand)
HINWEIS: Sequencer Resets sind im allgemeinen nur nötig, wenn wich- tige Taktgeber (siehe dazu auch TS.CMR, 3c4.01H bzw. MOWR, 3c2H) verstellt werden, die der Synchronisierung bedürfen.
Mit Einleitung eines Resets (RT = 01 oder RT = 10) stellt der Sequencer solange die Arbeit ein, bis ihm das Ende des Resets signalisiert wird (RT = 11).
Da der Sequencer u.a. für den RAM-Refresh der Grafikkarte verantwortlich ist, sollte der TS nicht länger als nötig im Resetzustand gehalten werden... ;)
Für die Dauer eines Resets werden keine Synchronisations- signale mehr generiert, d.h. die Bildschirmdarstellung wird abgeschaltet.
TS.CMR, Clocking Mode Register (3c4.01H) [RW] +++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |H| | |C| | | |V| |P| | |C| | | |S| |C| | |U| |?|?|D|?|S|?|?|8| +-+-+-+-+-+-+-+-+
CCU8 Character Clock Unit entspricht acht (1) oder neun (0) Pixeln.
HINWEIS: Dieses Bit sollte nur geändert werden, während der TS in einem synchronen Reset (TS.RR.RT, 3c4.00H[1..0] = 01) ge- halten wird.
HINWEIS: In einigen VGA-Dokumentation wird die Wirkung dieses Bits falsch angegeben (0 = 8, 1 = 9 Pixel).
HPCS Half Pixel Clock Select.
Ist dieses Bit gesetzt, wird der über MOWR.PCS (3c2H[2..3]) einge- stellte Pixeltakt halbiert, anderenfalls wird mit vollem PCS getak- tet.
HINWEIS: Dieses Bit sollte nur geändert werden, während der TS in einem synchronen Reset (TS.RR.RT, 3c4.00H[1..0] = 01) ge- halten wird.
VSD Video Signal Disable.
Durch Setzen des VSD-Bits wird der TS veranlasst, die Bildschirm- darstellung einzustellen. In diesem Zustand kann die CPU wesentlich schneller auf das Video-RAM zugreifen, als es sonst der Fall ist.
TS.WPMR, Write Plane Mask Register (3c4.02H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |P|P|P|P| | | | | |E|E|E|E| |?|?|?|?|3|2|1|0| +-+-+-+-+-+-+-+-+
PEn Plane Enable 0..3.
Schreibzugriff auf Plane n zulassen (1) oder unterbinden (0).
TS.CMSR, Character Map Select Register (3c4.03H) [RW] +++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | |H|L|H|H|L|L| | | |C|C|C|C|C|C| | | |M|M|M|M|M|M| |?|?|2|2|1|0|1|0| +-+-+-+-+-+-+-+-+
LCM Lo Character Map HCM Hi Character Map
Über die Bittripel (LCM: 0/1/4 bzw. HCM: 2/3/5) lässt sich frei be- stimmen, an welchen Offsets relativ zum Beginn der dritten 64 KByte Bitplane der VGA (Plane 2) der Character Generator die Zeichensatz- definitionen der beiden Zeichensätze für die Darstellung von ASCII- Codes entnehmen soll:
?CM Map Ofs
000 0 0000H 001 1 4000H 010 2 8000H 011 3 c000H 100 4 2000H 101 5 6000H 110 6 a000H 111 7 e000H
Für die, denen es noch nicht bekannt ist: Seit den EGAs werden in 16-farbigen Textmodi grundsätzlich zwei Zeichensätze 256 Zeichen- definitionen verwendet. Dabei wird die jeweilige Zeichensatzzustän- digkeit für jeden darzustellenden ASCII-Code anhand des Intensity- Bits (Bit 3) seines Farbattributes ermittelt.
Mit anderen Worten: Für ASCII-Codes in den Vordergrundfarben 0..7 wird ein anderer Zeichensatz (nämlich der über LCM festgelegte) be- nutzt, als für ASCII-Codes, deren Vordergrundfarbe im Bereich 8..15 liegt (für diesen Bereich ist der über HCM gewählte Zeichensatz zu- ständig).
Nach außen hin bleibt diese permanent vorhandene Unterscheidung je- doch oft unentdeckt, da das BIOS beim Init von 16-farbigen Textmodi normalerweise LCM = HCM setzt, und somit alle Vordergrundfarben den selben Zeichensatz benutzen... ;)
HINWEIS: Voraussetzung für die Funktionalität dieses Registers ist ein gesetztes TS.MMR.EEM-Bit (3c4.04H[1]).
HINWEIS: Um auf die Plane 2 lesend und/oder schreibend zugreifen zu können, sind natürlich diverse Umprogrammierungen (siehe dazu GDC.RMSR, (3ce.04H), TS.WPMR (3c4.02H), GDC.MISC.VMM (3ce.06H[2..3]), TS.MMR.OED (3c4.04H[2]), sowie GDC.MR.OEE (3ce.05H[4])) vonnöten... &)
TS.MMR, Memory Mode Register (3c4.04H) [RW] +++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | | | | | | | | | | |C|O|E|G| | | | | |4|E|E|F| |?|?|?|?|E|D|M|X| +-+-+-+-+-+-+-+-+
GFX Grafikmodus (1) oder Testmodus (0).
EEM Enable Extended Memory.
Dieses Bit existiert nur noch aus EGA-Kompatibilitätsgründen. Ein gesetztes Bit bewirkt die Freischaltung des VGA-RAMs vom 64sten bis zum 256sten KByte und ist bei VGAs daher i.d.R. immer gesetzt.
HINWEIS: Nur wenn dieses Bit gesetzt ist, lassen sich über das vor- stehend beschriebene Register TS.CSMR (3c4.03H) Zeichen- sätze auswählen.
OED Odd/Even Disable.
Ist dieses Bit gelöscht, arbeitet der TS im Odd/Even Mode, anderen- falls im linearen Modus.
Im Odd/Even Mode werden gerade Video-RAM Adressen auf die Planes 0 und 2, ungerade auf die Planes 1 und 3 gemapped. Im linearen Modus wird sequentiell innerhalb der Plane(s) gemapped, die laut Inhalt von TS.WPMR (3c4.02H[0..3]) aktiviert ist (sind).
HINWEIS: TS.MMR.OED und GDC.MR.OEE (3ce.05H[4]) sollten immer nur paarweise gewählt werden (Odd/Even: [OED = 0 und OEE = 1], Linear: [OED = 1 und OEE = 0]), sonst driften TS und GDC ¯etwas® auseinander... ;)
HINWEIS: Der Chain4 Mode besitzt höchste Priorität. Ist dieser Mo- dus aktiviert (TS.MMR.C4E = 1, 3ce.04H[3] = 1), werden die Odd/Even Steuerbits OED und GDC.MR.OEE ignoriert.
C4E Chain4 Enable.
Durch Setzen des C4E-Bits wird der Chain4 Mode aktiviert. Dieser Modus wird z.B. vom Mode 13H (320x200x256) benutzt, um das Video- RAM aus CPU-Sicht linear adressierbar zu machen (CRTC-intern wird weiterhin planeweise nMOD4 adressiert).
Andere Ports zur VGA-Programmierung -----------------------------------
Abschließend noch alle restlichen gebräuchlichen Ports, die sich keinem der fünf großen Bausteine (ATC, CRTC, DAC, GDC, TS) zuordnen lassen. Vorsicht, die meisten der hier beschriebenen Ports (bzw. deren Bits) haben ein Doppel- leben, d.h. sie sprechen beim Lesen/Beschreiben unterschiedliche Register an und daher sind auch die Bitdefinitionen entsprechend unterschiedlich:
Port 3c2H [R], IS0R, Input Status #0 Register +++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | |V| | |P| | | | | |I| | |C| | | | | |P|?|?|E|?|?|?|?| +-+-+-+-+-+-+-+-+
HINWEIS: Bitdefinitionen nur gültig bei LESEZUGRIFF. Schreibzugriff auf Port 3c2H adressiert dagegen das Miscellaneous Output Write Register (MOWR).
PCE Pixel Clock Enabled.
Ist dieses Bit gesetzt, ist der laut Miscellaneous Output Write Re- gister gewählte Taktgenerator aktiv (siehe MOWR.PCS, 3c2H[2..3]).
VIP Vertical Interrupt Pending.
Auf VGA-Karten, die die Fähigkeit besitzen, bei VSyncs IRQ 2 auszu- lösen, wird das Eintreten eines solchen Zustandes hier durch ein gesetztes VIP-Bit angezeigt.
Siehe hierzu auch die Bits CRTC.VRER.VID und .VIL (3d4.11H[4|5]).
Port 3c2H [W], MOWR, Miscellaneous Output Write Register ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6|5|4|3 2|1|0| | | | | | | | | H |O| | P |V|C| | V |E| | C |M|E| | P |B|?| S |E|E| +---+-+-+---+-+-+
HINWEIS: Bitdefinitionen nur gültig bei SCHREIBZUGRIFF. Lesezugriff auf Port 3c2H adressiert dagegen das Input Status #0 Re- gister (IS0R). Über Port 3c2H gesetzte Werte können über Port 3ccH (Miscellaneous Output Read Register, MORR) aus- gelesen werden.
CEE Color Emulation Enable.
Ist das CEE-Bit gesetzt, wird die Karte im Farbmodus, anderenfalls im Monochrommodus betrieben.
Bei CEE = 1 ist der CRTC über die Ports 3d4H/3d5H zu programmieren und es muß Port 3daH benutzt werden, um den Inhalt des Input Status #1 Registers (IS1R) lesen, bzw. das Feature Control Write Register (FCWR) beschreiben zu können.
Durch Löschen des CEE-Bits wird eine VGA-Karte MDA-kompatibel und die zu benutzenden Portadressen ändern sich zu 3b4H/3b5H und 3baH.
VME Video-RAM Modify Enable.
CPU-Zugriff auf Video-RAM zulässig (1) oder nicht (0).
PCS Pixel Clock Select.
00 25.175 MHz Clock (640 bzw. 320 Pixel) 01 28.322 MHz Clock (720 bzw. 360 Pixel) 10 reserviert (für externe Taktung mit anderen Frequenzen) 11 reserviert (für interne Resets)
HINWEIS: Diese Bits sollten nur geändert werden, während der TS in einem synchronen Reset (TS.RR.RT, 3c4.00H[1..0] = 01) ge- halten wird.
OEB Odd/Even Bank Select.
Bestimmt in O/E Modes (Videomodi 00..03H und 07H), ob die obere (1) oder untere (0) 64 KByte Bank aktiv ist.
HVP Horizontal/Vertical Sync Polarity.
Bit 6 bestimmt die horizontale, Bit 7 die vertikale Polarität von Sync-Signalen. Ein jeweils gesetztes Bit bedeutet negative, ein ge- löschtes positive Polarität. Die Kombination beider Polaritäten be- stimmt die Höhe der Display Area:
00 reserviert 01 400 Zeilen 10 350 Zeilen 11 480 Zeilen
HINWEIS: Diese Bits sollten nur geändert werden, während der TS in einem synchronen Reset (TS.RR.RT, 3c4.00H[1..0] = 01) ge- halten wird.
HINWEIS: In einigen VGA-Dokumentationen sind die Werte für 350 und 400 Zeilen vertauscht. Obiges ist definitiv richtig.
Port 3c3H, VSER, Video Subsystem Enable Register ++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | | | | |V| | | | | | | | |S| |?|?|?|?|?|?|?|E| +-+-+-+-+-+-+-+-+
VSE Video Subsystem Enable.
Das VSE-Bit kann man als Hauptschalter für die gesamte VGA-Karte verstehen. Durch Löschen des Bits werden Bilderzeugung und Darstel- lung abgeschaltet. Außerdem wird der Zugriff auf die VGA-Controller ganz bzw. teilweise unterbunden.
Port 3caH [R], FCRR, Feature Control Read Register ++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |V| | | | | | | | |S| | | | |?|?|?|?|S|?|?|?| +-+-+-+-+-+-+-+-+
VSS Vertical Sync Select. Nur durch Auslesen dieses VSS-Bits lässt sich der Zustand des FCWR.VSS-Bits (3daH[3]) ermitteln. Dieser Umweg ist erforderlich weil Port 3daH beim Schreiben zwar als Feature Control Write Register, beim Lesen aber als Input Status #1 Register dient.
Normalerweise, d.h. in Non-Interlaced Modi, ist VSS immer gelöscht.
Port 3ccH [R], MORR, Miscellaneous Output Read Register +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6|5|4|3 2|1|0| | | | | | | | | H |O| | P |V|C| | V |E| | C |M|E| | P |B|?| S |E|E| +---+-+-+---+-+-+
HINWEIS: Bitdefinitionen nur gültig bei LESEZUGRIFF. Gesetzt werden die hier auslesbaren Bits über Port 3c2H (MOWR, Miscellan- eous Output Write Register).
CEE Color Emulation Enable.
Ist das CEE-Bit gesetzt, wird die Karte im Farbmodus, anderenfalls im Monochrommodus betrieben.
Bei CEE = 1 ist der CRTC über die Ports 3d4H/3d5H zu programmieren und es muß Port 3daH benutzt werden, um den Inhalt des Input Status #1 Registers (IS1R) lesen, bzw. das Feature Control Write Register (FCWR) beschreiben zu können.
Durch Löschen des CEE-Bits wird eine VGA-Karte MDA-kompatibel und die zu benutzenden Portadressen ändern sich zu 3b4H/3b5H und 3baH.
VME Video-RAM Modify Enable.
CPU-Zugriff auf Video-RAM zulässig (1) oder nicht (0).
PCS Pixel Clock Select.
00 25.175 MHz Clock (640 bzw. 320 Pixel) 01 28.322 MHz Clock (720 bzw. 360 Pixel) 10 reserviert (für externe Taktung mit anderen Frequenzen) 11 reserviert (für interne Resets)
OEB Odd/Even Bank Select.
Bestimmt in O/E Modes (Videomodi 00..03H und 07H), ob die obere (1) oder untere (0) 64 KByte Bank aktiv ist.
HVP Horizontal/Vertical Sync Polarity.
Bit 6 enthält die horizontale, Bit 7 die vertikale Polarität von Sync-Signalen. Ein jeweils gesetztes Bit bedeutet negative, ein ge- löschtes positive Polarität. Aus der Kombination beider Polaritäten ergibt sich die Höhe der Display Area:
00 reserviert 01 400 Zeilen 10 350 Zeilen 11 480 Zeilen
HINWEIS: In einigen VGA-Dokumentationen sind die Werte für 350 und 400 Zeilen vertauscht. Obiges ist definitiv richtig.
Port 3daH [W], FCWR, Feature Control Write Register +++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |V| | | | | | | | |S| | | | |?|?|?|?|S|?|?|?| +-+-+-+-+-+-+-+-+
VSS Vertical Sync Select.
Im Normalfall, d.h. in Non-Interlaced Modi, ist VSS immer gelöscht.
HINWEIS: Über Port 3daH kann das VSS-Bit nur geschrieben werden, da er beim Lesen als Input Status #1 Register dient. Zum Aus- lesen ist ein Umweg über Port 3caH (FCRR) erforderlich.
Port 3daH [R], IS1R, Input Status #1 Register +++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5 4|3|2|1|0| | | | | | | | | | | | V |V| | |E| | | | T |R| | |R| |?|?| S |A|?|?|A| +-+-+---+-+-+-+-+
ERA Either Retrace Active.
Ist dieses Bit gesetzt, läuft gerade entweder ein horizontaler oder vertikaler Retrace. Ist das VRA-Bit ebenfalls gesetzt, handelt es sich um einen VRetrace, anderenfalls um einen HRetrace.
Der Zeitpunkt von ERA = 1 ist der ideale Zeitpunkt zur Manipulation aller möglichen VGA-Register, bzw. Kopierereien in die Fetch Area (des Video-RAMs), weil während jedes Retraces bekanntlich Display Enable inaktiv ist (somit kein Flimmern, Schnee, o.ä.).
VRA Vertical Retrace Active.
VRetrace aktiv (1) oder nicht (0).
VTS Video Test Status.
Über diese Bits lässt sich bei 100% VGA-kompatiblen Karten das Er- gebnis eines über ATC.CPER.VMUX (3c0.12H[4..5]) ausgelösten Video- Tests ermitteln.
VMUX Status der Farbbits/Leitungen
00 2 und 0 01 5 und 4 10 3 und 1 11 7 und 6 | +----> IS1R-Bit 4 -+ | +-- VTS +----------> IS1R-Bit 5 -+
Mode X (Tweaked Mode, Basis Videomodus 13H)
Juergen Thelen 2:2450/55.5:
Als IBM 1987 die VGA-Grafikkarte einführte, dürfte so mancher Programmierer überhaupt erst auf den PC aufmerksam geworden sein. Freunden der sogenannten Daddelkisten von Commodore, Atari, Nintendo usw. konnte der Sprung von ehe- mals 16 (bis EGA) auf dann 256 gleichzeitig darstellbare Farben im mit der VGA eingeführten Videomode 13H (320x200x256) zwar allenfalls ein müdes Läch- eln entlocken, aber der erste Schritt in Richtung Hi- bzw. True-Color war nun auch auf dem PC getan. Mode 13H entwickelte sich sehr schnell zu einer beliebten Spielwiese für alle möglichen PC-Software-Entwickler.
Allerdings blieben auch hier Wünsche offen. So ist das Video-RAM im Mode 13H zwar auf den ersten Blick sehr einfach organisiert (die 320x200 = 64.000 Pi- xel liegen zeilenweise nacheinander im Video-RAM, ein Byte pro Pixel), aber leider bietet der Mode 13H nur eine einzige Bildschirmseite.
Um flüssige und ruckelfreie Animationen zu bewerkstelligen, reicht aber nun mal in den meisten Fällen eine einzelne Bildschirmseite nicht aus. Genug RAM für mehrere Seiten hatte zwar bereits die originale VGA (256 KByte) - dort hätten also problemlos vier Seiten Platz gehabt - aber aus irgendwelchen un- erfindlichen Gründen entschieden sich die IBM-Leute damals dazu, die lineare Organisation des Video-RAMs nur nach aussen vorzugaukeln, intern (VGA-RAM) aber weiterhin planar zu arbeiten.
Im Klartext bedeutet das, daß im Mode 13H beispielsweise beim Schreiben des Bytes ffH an die Adresse a000:0007H das Byte zwar tatsächlich an Offset 7 im Video-RAM, aber im VGA-RAM in Plane 3 an Adresse 0004H landet.
Häh? Ägypten? ;)
Verantwortlich für dieses Chaos ist der Chain4-Modus, welcher im Mode 13H normalerweise aktiv ist. Im Chain4-Modus werden von der CPU kommende Offset- angaben (hier 0007H) als kodierte Plane-/Offsetangaben interpretiert:
Kodierung einer Video-RAM Offsetangabe im Mode 13H ++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2|1 0| | | | | | P | | | L | | Plane-relativer 0MOD4 Offset | N | +-----------------------------------------------------------+---+
Dabei wird nach folgendem Schema vorgegangen: Zunächst wird anhand der un- tersten zwei Bits die Zielplane bestimmt. In unserem Fall (0007H) sind die untersten beiden Bits gesetzt, folglich dient Plane 3 als Zielplane.
Im Anschluss an die Zielplanebestimmung werden die Planebits aus der Offset- angabe ausgeblendet (AND mit fffcH) und erst der daraus resultierende Wert dient als endgültige Offsetangabe relativ zum Beginn der Zielplane. Aus un- serer 0007H würde also eine 0004H...
Spätestens jetzt dürfte auch klar sein, warum der Mode 13H gar nicht mehr als eine einzige Bildschirmseite bieten kann. Wenn die untersten zwei Bits der Offset-Adresse immer ausgeblendet werden (0MOD4), wird natürlich auch nur jedes vierte Byte in einer Plane tatsächlich genutzt. Da die Planegröße in diesem Zusammenhang zwangsläufig auf 64 KByte (320x200 = 64.000 Byte ÷ 64 KByte) pro Plane fixiert ist, sind die 256 KByte RAM der originalen VGA na- türlich schon komplett verbraten, obwohl dabei 75% der RAM-Kapazität einfach ungenutzt den Bach runtergehen... &)
Nutzung des VGA-RAMs im Videomode 13H +++++++++++++++++++++++++++++++++++++
Plane 0 Plane 1 Plane 2 Plane 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+- |0|°|°|°|4|°|°|°|: |1|°|°|°|5|°|°|°|: |2|°|°|°|6|°|°|°|: |3|°|°|°|7|°|°|°|: +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
n Byte von Offset n im Video-RAM ° ungenutzt
Kein Wunder also, daß sich Heerschaften von Programmierern hinab in die Low- Level Tiefen der VGA-Ports begaben, um dieses Ackerland zu bestellen, sprich dem Mode 13H noch irgendwie weitere Bildschirmseiten zu entlocken.... 8)
Das Resultat dieser Bemühungen ist bekannt. Wer der erste Programmierer war, der einen Mode X realisiert hat, ist sehr schwer zu sagen. Wahrscheinlich waren es Dutzende gleichzeitig. Als erster veröffentlicht hat seine Schand- taten &) jedenfalls m.W. Michael Abrash (Dr. Dobbs Journal, USA). Wenn ich mich recht entsinne, das erste Mal in irgendeiner DDJ-Ausgabe von 1989. Daß einige Buchautoren bzw. Demo-Coder den Mode X zig Jahre später noch als gut- gehütetes Geheimnis bezeichnen, ist mir insofern etwas rätselhaft... ;)
Von Michael Abrash stammt übrigens auch die Bezeichnung "Mode X" (angelehnt an "Modus Unbekannt"). Er taufte ihn kurzerhand so, weil die IBM-Dokus nur vage Andeutungen über die Existenz eines solchen Modus machten und ihn im Prinzip gänzlich verschwiegen (wieso erinnert mich das bloß an Intel? ;))...
Mittlerweile existieren unzählige Varianten des Mode X. Als sicher (insofern sicher, daß keine Timingprobleme mit VGA und/oder Monitor zu erwarten sind) gelten übrigens oft nur zwei (bzw. drei) bestimmte Varianten des Mode X und zwar: 320x200, 320x400, sowie mit Abstrichen 320x480.
Nicht nur meiner Erfahrung nach lässt sich dieses Spektrum aber durchaus auf insgesamt acht Varianten erweitern:
- 320x200, min. 4 Seiten - 320x240, min. 3 Seiten - 320x400, min. 2 Seiten - 320x480, max. 1 Seite - 360x200, min. 3 Seiten - 360x240, min. 3 Seiten - 360x400, max. 1 Seite - 360x480, max. 1 Seite
Die Seitenangaben beziehen sich natürlich auf die originale VGA (256 KByte), um das größtmögliche Spektrum von Grafikkarten abzudecken. 320x480, 360x400 und 360x480 können übrigens normalerweise nie mehr als eine einzelne Seite haben, auch dann nicht, wenn die Karte z.B. 2 MByte RAM hätte.
Das kommt daher, weil das CPU-Adressraum-Fenster, in welches das Video-RAM eingeblendet wird, normalerweise generell 65.536 Bytes groß ist. Somit kön- nen im Mode X also auch immer nur maximal 65536 * 4 (Planes) = 262.144 Pixel über das Video-RAM adressiert werden, egal wie groß das VGA-RAM ist...
Sollen mindestens zwei Bildschirmseiten zur Verfügung stehen, darf folglich die Auflösung eine Gesamtpunktzahl von (65536 * 4) / 2 = 131.072 Pixel nicht überschreiten. Wie man leicht nachrechnen kann, liegen die drei erwähnten Varianten jedoch über dieser Grenze (320x480 = 153.600, 360x400 = 144.000, 360x480 = 172.800), also is' nix mit Pageflipping... ;)
Es gibt zwar ein paar Karten, bei denen sich auch im Mode X a000H- u. b000H- Segment aneinanderkoppeln lassen (Maximalauflösung für Pageflipping liegt in diesem Fall bei 65536 * 4 (Planes) = 262.144 Pixeln), aber erstens sind die- se Karten wie gesagt eher rar und zweitens ist der Zugriff meist sehr auf- wendig und langsam (zumindest im RM bei segmentiertem Speichermodell)...
Togal. Kommen wir zur Technik:
Nach der Aktivierung des Videomode 13H müssen wir als erstes also irgendwie für die Wiederaufhebung des Chain4-Modus sorgen. Dies lässt sich sehr simpel durch Löschen des TS.MMR.C4E-Bits (3c4.04H[3]) erreichen. Nach Löschen des Bits werden Offsetangaben bei CPU-Zugriffen auf das Video-RAM von der VGA ab sofort nicht mehr als kodierte Plane-/Offsetangaben, sondern nur noch als reine Offsetangaben relativ zur momentan aktivierten Plane interpretiert.
Das bedeutet natürlich gleichzeitig, daß sich der Programmierer von nun an selbst um die Aktivierung der richtigen Schreib- bzw. Lese-Plane zu kümmern hat, bevor zugegriffen wird, aber dazu später mehr...
Zunächst müssen wir uns nämlich noch um ein bisher nicht behandeltes Pro- blem kümmern. Durch Löschen des TS.MMR.C4E-Bits haben wir aus CPU-Sicht zwar den Chain4-Modus deaktiviert, aber der CRTC wird (aufgrund der bei Initiali- sierung von Videomode 13H vorgenommenen Grundeinstellungen) trotzdem weiter- hin nur jedes vierte Planebyte benutzen. Ursache hierfür ist, daß der CRTC im Videomode 13H mit DWord-Addressing arbeitet, was ja im Chain4-Modus auch korrekt wäre. Den haben wir aber gerade deaktiviert... 8)
Folglich liegen unsere Pixelbytes in den Planes auch keine 4 Bytes (DWord) mehr aus-, sondern unmittelbar hintereinander (Byte). Ergo müssen wir den CRTC irgendwie dazu bewegen, vom DWord- ins Byte-Addressing umzuschalten. Zu diesem Zweck löscht man ZUERST das CRTC.ULR.ADR4-Bit (3d4.14H[6]) und DANACH das CRTC.MCR.ADR1-Bit (3d4.17H[6]). Vorsicht, diese Reihenfolge ist wichtig!
Das ADR4-Bit hat auf so einigen VGAs eine höhere Priorität als das ADR1-Bit, d.h. solange ADR4 noch gesetzt ist, kann man an ADR1 soviel rumfummeln, wie man will, die VGA wird es geflissentlich ignorieren... B)
Wie dem auch sei, haben wir einmal Chain4 ab-, DWord-Adressing aus- und das Byte-Adressing eingeschaltet, sieht das VGA-RAM nun so aus:
Nutzung des VGA-RAMs im Mode X ++++++++++++++++++++++++++++++
Plane 0 Plane 1 Plane 2 Plane 3
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 +-+-+-+-+- +-+-+-+-+- +-+-+-+-+- +-+-+-+-+- |0|4|8|c|: |1|5|9|d|: |2|6|a|e|: |3|7|b|f|: +-+-+-+-+ +-+-+-+-+ +-+-+-+-+ +-+-+-+-+
n Byte von Offset n im Video-RAM
Die bisher erläuterten Schritte gelten für alle acht Mode X Varianten, auf die in diesem Topic eingegangen wird. Ob, und wenn ja, welche zusätzlichen Schritte notwendig sind, um eine bestimmte Variante zu initialisieren, hängt von der gewählten Auflösung ab, und wird im nachfolgenden beschrieben:
320x200, 4 Seiten -----------------
- Videomode 13H aktivieren - Chain4-Modus aus (TS.MMR.C4E, 3c4.04H[3] = 0) - DWord-Addressing aus (CRTC.ULR.ADR4, 3d4.14H[6] = 0) - Byte-Addressing ein (CRTC.MCR.ADR1, 3d4.17H[6] = 1)
Für's Setup keine weiteren Schritte erforderlich.
320x400, 2 Seiten -----------------
- Videomode 13H aktivieren - Chain4-Modus aus (TS.MMR.C4E, 3c4.04H[3] = 0) - DWord-Addressing aus (CRTC.ULR.ADR4, 3d4.14H[6] = 0) - Byte-Addressing ein (CRTC.MCR.ADR1, 3d4.17H[6] = 1) * Double-Scanning aus
Wie man sieht, ist nur ein neuer Punkt hinzugekommen, der sogenannte Double- Scan. Unter Double-Scan versteht man die doppelte Ausgabe jeder Bildschirm- zeile, die vom CRTC aus der Fetch Area des Video-RAMs gelesen wird.
Im Videomodus 13H wird dieser Double-Scan dazu benutzt, nach aussen hin eine 320x200 Auflösung vorzugaukeln, obwohl intern eigentlich ein 400-Zeilenmodus aktiv ist (die VGA kann nämlich nur 350, 400 und 480-Zeilenmodi generieren).
Schaltet man den Double-Scan aus, wird dann tatsächlich eine 320x400 Auflö- sung aktiv. Allerdings ist das nicht ganz problemlos. Ursprünglich war zur Steuerung des Double-Scans nämlich einmal Bit 7 des Maximum Scan Line Regis- ters (CRTC.MSLR.DBLS, 3d4.09H[7], 1 = ein, 0 = aus) vorgesehen.
Benutzt wird das Bit heutzutage kurioserweise von nahezu keinem VGAClone. In den meisten Fällen wird man heute nach einem Videomode 13H Init ein gelösch- tes CRTC.MSLR.DBLS-Bit antreffen (was eigentlich falsch ist) und stattdessen eine 1 im "Scan Lines Per Row"-Feld (CRTC.MSLR.SLPR, 3d4.09H[0..4]) finden, die dann die Verdopplung bewerkstelligt.
Da einige Hersteller das Maximum Scan Line Register gar gänzlich verschmähen und stattdessen den Double-Scan über das Mode Control Register, genauer über das "Line Count by 2"-Bit (CRTC.MCR.LCB2, 3d4.17H[2]), steuern, empfiehlt es sich u.U. also, alle drei Möglichkeiten zu beachten, falls die angesteuerte VGA zum Programmierzeitpunkt nicht als bekannt vorausgesetzt werden kann:
1. CRTC.MSLR.DBLS, 3d4.09H[7] = 0 2. CRTC.MSLR.SLPR, 3d4.09H[0..4] = 0 3. CRTC.MCR.LCB2, 3d4.17H[2] = 0
320x480, 1 Seite ----------------
- Videomode 13H aktivieren - Chain4-Modus aus (TS.MMR.C4E, 3c4.04H[3] = 0) - DWord-Addressing aus (CRTC.ULR.ADR4, 3d4.14H[6] = 0) - Byte-Addressing ein (CRTC.MCR.ADR1, 3d4.17H[6] = 1) - Double-Scanning aus (siehe 320x400) * Vertikales Timing auf 480-Zeilenmodus anpassen
Bei allen weiteren Mode X Varianten (320x200 und 320x400 ausgenommen) sind nun noch diverse Eingriffe in das vertikale und/oder horizontale Timing er- forderlich.
WARNUNG: Eingriffe in das vertikale/horizontale Videotiming sind nicht unge- fährlich (siehe dazu auch das CRTC-Kapitel in BAW0019). Aus diesem Grund sind die verantwortlichen Timingregister (CRTC, 00H bis 07H) nach dem Initialisieren von Videomode 13H auch schreibgeschützt.
Der Schreibschutz kann durch Löschen des CRTC.VRER.R07WD-Bits, also durch 3d4.11H[7] = 0, aufgehoben werden. Für alles, was ihr danach auch immer in die Register 00H bis 07H schreibt, seid ihr natürlich ausschließlich alleine verantwortlich... >-)
Ok, soviel zum Gefahrenhinweis...
Um den 480-Zeilenbetrieb erst einmal grundsätzlich zu aktivieren, müssen die Bits 6 und 7 im Miscellaneous Output Write Register (MOWR.HVP, 3c2H[6..7]) gesetzt werden.
Da diese Umstellung einen erheblichen Eingriff in die Videosynchronisation darstellt, sollte man sie keinesfalls vornehmen, ohne dem Timing Sequencer die Chance zu geben, sich neu zu synchronisieren.
Dazu leitet man zuerst einen Reset ein (TS.RR.RT, 3c4.00H[1..0] = 01b), set- zt danach den 480-Zeilenbetrieb (MOWR.HVP, 3c2H[7..6] = 11b) und teilt dann abschließend dem Timing Sequencer durch TS.RR.RT, 3c4.00H[1..0] = 11b mit, daß er jetzt den Reset beenden und sich neu synchronisieren kann.
So. Nun sind aber die vertikalen Timingwerte des CRTCs natürlich immer noch auf den 400-Zeilenbetrieb abgestimmt sind. Ergo müssen die als nächste dran glauben... ;)
Die nachfolgend angeführten Werte zur Umprogrammierung von Vertical Total, Vertical Display End, Vertical Blank Start, Vertical Retrace Start, Vertical Retrace End und Vertical Blank End sind nicht bindend, sie haben sich in der Praxis einfach nur als allgemein "kompatibel" erwiesen:
Vertical Timing Wert Bits Register an Port 3d4H Line#
º Total 525 0..9 in 06H[0..7], 07H[0], 07H[5] (524) º Display End 479 0..9 in 12H[0..7], 07H[1], 07H[6] (479) º Blanking Start 487 0..9 in 15H[0..7], 07H[3], 09H[5] (487) º Retrace Start 490 0..9 in 10H[0..7], 07H[2], 07H[7] (490) º Retrace End 12 0..3 in 11H[0..3] (492) º Blanking End 6 0..6 in 16H[0..6] (518)
320x240, 3 Seiten -----------------
- Videomode 13H aktivieren - Chain4-Modus aus (TS.MMR.C4E, 3c4.04H[3] = 0) - DWord-Addressing aus (CRTC.ULR.ADR4, 3d4.14H[6] = 0) - Byte-Addressing ein (CRTC.MCR.ADR1, 3d4.17H[6] = 1) - Vertikales Timing auf 480-Zeilenmodus anpassen (siehe 320x480)
Für den Setup dieser Variante ist nichts Neues hinzugekommen. Im Prinzip ist der Setup genau der gleiche, wie der für 320x480. Der einzige Unterschied besteht darin, daß wir für 320x240 einfach den Double-Scan aktiv lassen. Wir benutzen also den gleichen Trick, wie ihn die VGA für den Mode 13H einsetzt ("getürkter" 320x200, real 320x400)... B)
360x200, 3 Seiten -----------------
- Videomode 13H aktivieren - Chain4-Modus aus (TS.MMR.C4E, 3c4.04H[3] = 0) - DWord-Addressing aus (CRTC.ULR.ADR4, 3d4.14H[6] = 0) - Byte-Addressing ein (CRTC.MCR.ADR1, 3d4.17H[6] = 1) * Horizontales Timing auf 28 MHz-Modus anpassen
Was jetzt kommt, war naheliegend. Wenn man die Y-Auflösung der VGA verändern kann, dürfte das mit der X-Auflösung doch ebenso funktionieren, oder?
Na, lokus... ;)
Ansatzpunkt ist auch hier, ähnlich wie bei der Erhöhung der Y-Auflösung, das Miscellaneous Output Write Register. Mode 13H wird normalerweise mit einem Pixeltaxt von ungefähr 25 MHz betrieben. Erhöhen wir durch Setzen von Bit 2 (MOWR.PCS, 3c2H[2..3]) den Pixeltakt auf ca. 28 MHz, erweitern wir damit un- ser X-Spektrum von 320 (eigentlich 640/2) auf 360 (eigentlich 720/2) Pixel.
Daß die Veränderung des Pixeltaktes ebenso der Synchronisation bedarf, wie die unter 320x480 beschriebene Umstellung in den 480-Zeilenbetrieb, dürfte wohl einleuchten. Also erst Reset einleiten (TS.RR.RT, 3c4.00H[1..0] = 01b), dann umstellen (MOWR.PCS, 3c2H[3..2] = 01b) und abschließend dem Timing Se- quencer durch TS.RR.RT, 3c4.00H[1..0] = 11b mitteilen, daß er jetzt den Re- set beenden und sich neu synchronisieren kann.
Natürlich ist auch hier eine Anpassung des Videotimings des CRTCs erforder- lich, in diesem Fall aber eben die des horizontalen, nicht die des vertika- len. Besonders hingewiesen sei hier noch einmal auf die Gefahren, die Ein- griffe ins Videotiming (siehe BAW0019, CRTC-Kapitel) mit sich bringen, sowie den Fakt, daß der CRTC die Verwaltung des Horizontal Timings über Character Clock Units (CCU) abwickelt (im Videomode 13H (TS.CMR.CCU8, 3c4.01H[0] = 1) entspricht eine CCU normalerweise 8 Pixeln).
Ebenso wie die Werte für das vertikale 480-Zeilentiming sind auch die nach- folgenden Werte für die horizontale Anpassung auf 360 Pixel einfach nur be- währte Erfahrungswerte, und keineswegs bindend:
Horizontal Timing Wert Bits Register an Port 3d4H CCU#
º Total 107 0..7 in 00H[0..7] (111) º Display End 89 0..7 in 01H[0..7] ( 89) º Blanking Start 90 0..7 in 02H[0..7] ( 90) º Retrace Start 94 0..7 in 04H[0..7] ( 94) º Retrace End 10 0..4 in 05H[0..4] (106) º Blanking End 46 0..5 in 03H[0..4], 05H[7] (110)
Abschließend sollte man noch so nett sein, und dem CRTC mitteilen, daß sich die Zeilenlängen in der Fetch Area des Video-RAMs entsprechend ändern sol- len, denn wir haben ja jetzt nicht mehr 320 / 4 = 80, sondern 360 / 4 = 90 Bytes pro Zeile.
Nicht leichter als das, durch ein CRTC.OFSR.AUPVL, 3d4.13H = 45 ist die Kuh vom Eis (45 deshalb, weil bei Byte-Adressing eine Adress Unit 2 Bytes lang ist und wir 4 Planes haben -> 45 * 2 * 4 = 360)... 8)
360x400, 1 Seite ----------------
- Videomode 13H aktivieren - Chain4-Modus aus (TS.MMR.C4E, 3c4.04H[3] = 0) - DWord-Addressing aus (CRTC.ULR.ADR4, 3d4.14H[6] = 0) - Byte-Addressing ein (CRTC.MCR.ADR1, 3d4.17H[6] = 1) - Double-Scanning aus (siehe 320x400) - Horizontales Timing auf 28 MHz-Modus anpassen (siehe 360x200)
Nix Neues, 360x400 ist nur eine Kombination aus 320x400 und 360x200... ;)
360x480, 1 Seite ----------------
- Videomode 13H aktivieren - Chain4-Modus aus (TS.MMR.C4E, 3c4.04H[3] = 0) - DWord-Addressing aus (CRTC.ULR.ADR4, 3d4.14H[6] = 0) - Byte-Addressing ein (CRTC.MCR.ADR1, 3d4.17H[6] = 1) - Double-Scanning aus (siehe 320x400) - Vertikales Timing auf 480-Zeilenmodus anpassen (siehe 320x480) - Horizontales Timing auf 28 MHz-Modus anpassen (siehe 360x200)
Und wieder nix Neues. Kombi aus 320x400, 320x480 und 360x200... 8)
360x240, 3 Seiten -----------------
- Videomode 13H aktivieren - Chain4-Modus aus (TS.MMR.C4E, 3c4.04H[3] = 0) - DWord-Addressing aus (CRTC.ULR.ADR4, 3d4.14H[6] = 0) - Byte-Addressing ein (CRTC.MCR.ADR1, 3d4.17H[6] = 1) - Vertikales Timing auf 480-Zeilenmodus anpassen (siehe 320x480) - Horizontales Timing auf 28 MHz-Modus anpassen (siehe 360x200)
Na? Genau: auch hier nix Neues. Kombi aus 320x480 und 360x200... &)
So, die Setups der acht Mode X Varianten dürften jetzt wohl klar sein. Gehen wir also nun zur Anwendungsseite über:
Ermittlung der Zeilenlänge im Video-RAM ---------------------------------------
Ähm, ok... nicht gerade eine anspruchsvolle Aufgabe. Sie soll aber speziell den Einsteigern auch nur noch einmal verdeutlichen, daß wir es im Mode X mit einem Videomodus zu tun haben, der vier separate Planes besitzt.
Einige Einsteiger (speziell solche, die mit irgendwelchen (gerippten) Mode X Init Sources arbeiten, deren Auswirkungen aber nicht verstanden haben) nehm- en z.B. häufig fälschlicherweise an, daß auch im Mode X eine Zeile im Video- RAM immer noch der X-Auflösung entspricht (wie im Mode 13H halt).
Das ist natürlich nicht der Fall. Wie wir wissen, wird durch die Deaktivier- ung des Chain4-Modus (und der daraus resultierenden Verantwortlichkeitsüber- tragung der Plane-Aktivierung von der VGA an den Programmierer) die Zeilen- länge im Video-RAM automatisch (und in JEDEM Mode X) geviertelt. Im VGA-RAM entspricht die Länge der Zeile natürlich immer noch der X-Auflösung, sie ist aber eben über vier Planes verteilt. Ergo:
LineLength = Bildschirmbreite in Pixeln DIV 4 = ? Bytes
Ermittlung der Seitenlänge im Video-RAM ---------------------------------------
Bei der Ermittlung der Seitenlänge, die wir z.B. für das Umschalten zwischen mehreren Seiten (sogenanntes Pageflipping) benötigen, müssen wir logischer- weise auch wieder beachten, daß die Zeilenlänge im Mode X immer nur 1/4 der X-Auflösung beträgt. Demzufolge gilt:
PageLength = LineLength * Bildschirmhöhe in Pixeln = ? Bytes
Ermittlung der maximalen Seitenanzahl im Video-RAM --------------------------------------------------
Da wir wissen, daß nie mehr als 65.536 Bytes auf einmal im Video-RAM unter- gebracht werden können, haben wir natürlich auch kein Problem auszurechnen, wieviele Seiten beim unmittelbaren Hintereinanderlegen einer gegebenen Auf- lösung maximal im Video-RAM Platz finden würden:
MaxPages = 65536 DIV PageLength
Aktivieren einer bestimmten Seite (Pageflip) --------------------------------------------
Die Aktivierung einer bestimmten Seite führt bei Mode X Einsteigern relativ häufig zur Konfusion. Ursache dafür ist, daß der Programmierer die Lage sei- ner Seiten im Video-RAM (nahezu) völlig frei bestimmen kann.
Um trotzdem einen Ansatzpunkt zu bieten, geht die folgende Formel der Ein- fachheit halber davon aus, daß die erste Seite (Page 0) im Video-RAM immer bei Offset 0000H beginnt, die zweite Seite (Page 1) sich unmittelbar an das Ende von Page 0 anschließt, usw.:
PageAddress = PageNo * PageLength
Um die Seite an der errechneten Adresse zu aktivieren, sind lediglich zwei Register des CRTCs mit dem Hi- bzw. Lo-Anteil dieser Adresse zu füttern:
CRTC.LSAHR.LSA8F (3d4.0cH) = PageAddress DIV 256 CRTC.LSALR.LSA07 (3d4.0dH) = PageAddress MOD 256
Punktkoordinate (X, Y) in einen Video-RAM Offset umrechnen ----------------------------------------------------------
Um den Farbwert eines bestimmten Pixels an einer bestimmten Koordinate aus- lesen bzw. schreiben zu können, müssen wir in beiden Fällen zunächst einmal die entsprechende Offsetadresse im Video-RAM errechnen. Auch hier ist wieder zu beachten, daß im Mode X mit vier Planes gearbeitet wird. Das bedeutet na- türlich auch, daß immer vier Pixel über ein- und denselben Video-RAM Offset adressierbar sind. Welche(s) Pixel schließlich tatsächlich adressiert werden (wird), hängt natürlich davon ab, welche Plane(s) der Programmierer vor dem Zugriff aktiviert bzw. zu diesem Zeitpunkt bereits aktiviert ist (sind).
Doch hier erst einmal nur die Berechnung des Video-RAM Offsets:
VOfs = PageAddress + (Y * LineLength) + (X DIV 4)
Plane einer bestimmten Punktkoordinate (X, Y) ermitteln -------------------------------------------------------
Wie bereits zuvor erwähnt, werden im Mode X immer jeweils vier Pixel über ein- und denselben Video-RAM Offset angesprochen. Um auf ein bestimmtes die- ser vier Pixel zugreifen zu können, müßen wir ermitteln, in welcher Plane es sich befindet und diese vor dem Zugriff aktivieren.
Da der Mode X eine nMOD4-Adressierung benutzt (jedes Pixel wird entsprechend seiner linearen Adresse / 4 = n auch in Plane n abgelegt), entsprechen die untersten beiden Bits der X-Koordinate natürlich auch zwangsläufig immer der korrespondierenden Plane. Folglich gilt:
PlaneNo = X AND 3 = 0..3
Pixel an einer bestimmten Punktkoordinate (X, Y) auslesen ---------------------------------------------------------
Sind Video-RAM Offset und Plane einer bestimmten Punktkoordinate einmal er- mittelt, ist es natürlich auch kein Problem mehr, darauf zuzugreifen. Jetzt müssen wir nur noch dafür sorgen, daß die Plane auch aktiv ist, bevor wir das Video-RAM bei a000H:VOfs auslesen.
Nichts leichter als das. Die Auswahl der Read Plane wird über ein bestimmtes Bitfeld des Read Map Select Register im Graphics Data Controller gesteuert:
GDC.RMSR.RP, (3ce.04H[0..1]) = PlaneNo Color = Mem[a000H:VOfs]
Pixel an eine bestimmte Punktkoordinate (X, Y) schreiben --------------------------------------------------------
Das Schreiben eines Pixels funktioniert im Prinzip genauso, wie das im vor- stehenden Absatz beschriebene Lesen eines Pixels: sind Video-RAM Offset und Plane einmal bekannt, muß nur noch die entsprechende Plane aktiviert werden.
Allerdings ist es beim Schreiben (im Gegensatz zum Lesen) möglich, mehrere Planes gleichzeitig zu aktivieren. Das zuständige Register für die Wahl der Schreibplane(s) befindet sich auch nicht im GDC (wie beim Lesen), sondern im Timing Sequencer und nennt sich Write Plane Mask Register.
Da wir es jetzt mit einer ¯Mask® und nicht mit einem ¯Select® zu tun haben, muß die zuvor ermittelte PlaneNo natürlich auch erst entsprechend vorberei- tet werden, bevor sie ins Register geschrieben werden kann:
TS.WPMR.PEn, (3c4.02H[0..3]) = 1 SHL PlaneNo Mem[a000H:VOfs] = Color
Zum Schreiben von Einzelpixeln noch ein wichtiger Hinweis: Standardmäßig ist im Mode X (verursacht durch die Grundeinstellungen des Mode 13H) meist der Write Mode 0 aktiv (siehe dazu auch GDC.MR, 3ce.05H im Topic BAW0019).
Write Mode 0 ist i.d.R. etwas langsamer als Write Mode 1, da im Mode 0 mehr interne Verknüpfungen durchgeführt werden müssen, als es im Mode 1 der Fall ist. Zudem lassen sich im Write Mode 1 (in Verbindung mit Read Mode 0) sehr schnelle Blocktransfers innerhalb des Video-RAMs realisieren.
Seite löschen -------------
Die im Abschnitt zuvor beschriebene Methode zum Setzen von Einzelpixeln wäre zwar auch zum Löschen ganzer Bildschirmseiten verwendbar, aber wir wären na- türlich dumm, würden wir nicht den Umstand ausnutzen, daß beim Schreiben von Pixel nicht nur eine, sondern auch mehrere Planes gleichzeitig beschrieben werden können.
Folglich schalten wir durch ein TS.WPMR.PEn, 3c4.02H[0..3] = 0fH natürlich direkt alle vier Planes gleichzeitig ein. Danach reichen ¯PageLength® Byte- Writes in das Video-RAM, um die ganze Seite zu löschen (bzw. sie mit einem bestimmten Farbbyte zu füllen).
Dieses Prinzip kann natürlich auch für Pattern Fills, zum Zeichnen von hori- zontalen Linien, zum Füllen von Polygonen, usw. "mißbraucht" ;) werden. Der Geschwindigkeitsvorteil bleibt dabei nahezu immer unbeschnitten erhalten.
Hmmm... ich glaube, mehr gibt es zum Thema Mode X nicht zu sagen. Doch, eins fällt mir noch ein: die heute üblichen VGAs/SVGAs beherrschen fast alle VBE 2.0, oder sind zumindest über Hilfsmittel (wie z.B. UniVBE/SDD) auf diesen Stand zu bringen. Bei diesen Karten lohnt sich imo immer ein Test, ob es zum angestrebten Mode X nicht ein VESA-Gegenstück gibt, und falls ja, welcher von beiden Modi denn nun tatsächlich schneller ist. Je nach Anwendung, CPU, RAM und Grafikkarte kommt es da häufig zu den kuriosesten Ergebnissen... ;)
In diesem Sinne, have fun... 8)
P.S.: Auf mehrfachen Wunsch existiert nun auch eine Beispielsource (LSG0010) zum Initialisieren aller hier beschriebenen Mode X Varianten.
P.S.S.: Mehreren Lesern war die LSG0010 zu lang und/oder zu kompliziert und baten um individuelle Beispielsources in Kurzform.
Ok, ok, überredet... ;)
Ab sofort gibt es auch für jede Mode X Variante eine Beispielsource, die nur das notwendigste beinhaltet (LSG0011..LSG0018).
VBE 2.0 (VESA BIOS Extension)
Juergen Thelen 2:2450/55.5:
Kurz nach Einführung der VGA begann für die Programmierer leider auch wieder einmal ein Kompatibilitäts-Dilemma. Die meisten Hersteller von VGA-Clones begnügten sich nämlich natürlich nicht damit, nur die VGA-Fähigkeiten zu ko- pieren, sondern versuchten ihre Chips gleich noch um ein paar Funktionen zu erweitern. Solche erweiterten VGA-Chips nennt man heute im allgemeinen SVGAs (Super VGAs). Der Begriff SVGA bezeichnet also keinen Hardware-Standard, wie häufig fälschlicherweise angenommen wird, sondern ist einfach nur ein Deck- mäntelchen für alle jene Chips, welche dem VGA-Hardwarestandard folgen, aber zusätzlich noch mehr bieten. Wieviel mehr und was mehr ist dabei weder ge- normt, noch spielt es irgendeine Rolle...
Da jeder SVGA-Hersteller selbst festlegt, was sich denn zusätzlich zum VGA- Standard noch so alles auf seinen Chips abspielt, und vor allem, wie es das tut, war das Chaos logischerweise vorprogrammiert.
Zum Glück sahen die damals führenden Hersteller von Grafikchips sehr schnell ein, das irgendein Standard her musste. Zu diesem Zweck wurde kurzerhand die VESA (Video Electronics Standards Association) ins Leben gerufen, deren SCC (Software Standards Committee) wiederum (u.a.) eine Arbeitsgruppe namens VBE (VESA BIOS Extensions) bildete.
Diese VBE-Arbeitsgruppe machte sich dann flugs daran, eine standardisierte API (Application Programmers Interface, Softwareschnittstelle) aufzubauen, die es dem Programmierer ermöglichen sollte, alle möglichen SVGAs mit ein- und denselbem Satz Standardfunktionen ansprechen zu können, ohne deren In- terna (Low Level) kennen und/oder unterscheiden zu müssen.
Wie jede API bietet natürlich auch diese API den Vorteil der Universalität auf Kosten der Zugriffsgeschwindigkeit. Es wird nunmal nie etwas schnelleres geben, als direkte (Low Level) Hardwareprogrammierung... ;)
Togal. Ausgehend von der ursprünglichen (spartanischen) VBE 1.0 kam es dann immer wieder zu Erweiterungen. Es folgte die VBE 1.1, die erstmals erwei- terte Textmodi ins Spiel brachte und verbesserte Funktionen für das Double- Buffering und das Handling der Logical Line Length bot. Mit VBE 1.2 wurden zahlreiche Hi- und True-Color Video Modes integriert und entsprechende Funk- tionen für die RAMDACs eingebracht. Seit November 1994 schließlich ist die VBE 2.0 Stand der Dinge. Die wichtigsten Neuerungen der VBE 2.0 waren dabei die Standardisierung des sogenannten LFB-Speichermodells (Linear oder Flat Frame Buffer genannt) und die Einführung der Protected Mode API (siehe dazu Funktion 4f0aH).
Das LFBM kann (speziell in hohen Auflösungen) einen sehr hohen Performance- gewinn bringen. Grund genug also, daß es als gesondertes Topic (s. BAW0022) beschrieben wird... ;)
Nun aber genug zur Geschichte. Kommen wir zur VBE-API:
Eine VBE kann sich direkt im ROM des VGA BIOS befinden, oder wird als Trei- berdatei oder TSR-Programm vom Hersteller der Grafikkarte mitgeliefert. Egal ob nun Hardware- oder Software-VBE, der Zugriff erfolgt grundsätzlich erst einmal über den normalen Video-Interrupt INT 10H. Die ISR von Interrupt 10H unterscheidet dabei "normale" von VBE-API-Funktionen, indem sie den Wert des AH-Registers überprüft. Ist AH = 4fH, handelt es sich um eine VBE-Funktion und es wird entsprechend der Funktionsnummer im AL-Register verzweigt.
Natürlich ist ein Zugriff über einen Interrupt-Vektor nicht gerade schnell, aber zumindest im Real Address Mode (RM) bleibt einem da leider keine große Wahl. Im Protected Mode sieht es da, wie bereits erwähnt, schon etwas besser aus (siehe Funktion 4f0aH). Wie dem auch sei, here goes... ;)
Funktion 00H, Return VBE Controller Information -----------------------------------------------
Parameter:
AX = 4f00H
ES:DI = Zeiger auf zu füllende VBEInfoBlock-Struktur
HINWEIS: Füllung u.U. abhängig vom Inhalt des VBESignature-Feldes.
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
ES:DI = gefüllte VBEInfoBlock-Struktur (nur wenn AX = 004fH)
{ Struktur: VBEInfoBlock
Ofs Typ Bezeichnung
000H 4 B VBESignature 004H W VBEVersion 006H D OEMStringPtr 00aH 4 B Capabilities 00eH D VideoModePtr
--- VBE 1.0 --------------------------------------------------------------
012H 238 B Reserved
--- VBE 1.1+ -------------------------------------------------------------
012H W TotalMemory 014H 236 B Reserved
--- VBE 2.0 --------------------------------------------------------------
012H W TotalMemory 014H W OEMSoftwareRev 016H D OEMVendorNamePtr 01aH D OEMProductNamePtr 01eH D OEMProductRevPtr 022H 222 B Reserved 100H 256 B OEMData
}
VBESignature, Offset 000H, 4 Byte:
Bis einschließlich VBE 1.2 ist dieses Feld ein reines Rückgabefeld. Nach erfolgreichem Aufruf der Funktion hat die VBE es mit den Buchstaben 'VESA' (56H, 45H, 53H, 41H) gefüllt.
Gleiches geschieht im Erfolgsfall zwar auch bei VBE 2.0, aber bei dieser Version kann der Programmierer vor dem Aufruf über VBESignature bestimmen, ob der VBEInfoBlock mit Daten gemäß der VBE 1.2 oder VBE 2.0 gefüllt wer- den soll.
Soll der VBEInfoBlock mit Daten gemäß VBE 2.0 gefüllt werden, muß vor dem Aufruf 'VBE2' (56H, 42H, 45H, 32H) als VBESignature eingetragen werden. In allen anderen Fällen würde gemäß VBE 1.2 gefüllt.
VBEVersion, Offset 004H, Word:
Versionsnummer der VBE als BCD. Das Hi-Byte trägt dabei die Hauptversions- nummer, das Lo-Byte die Unterversionsnummer:
0100H VBE 1.0 0101H VBE 1.1 0102H VBE 1.2 0200H VBE 2.0
HINWEIS: Es tauchen immer wieder Programme auf, die VBEVersion nicht als BCD, sondern fälschlicherweise als eine Dezimalzahl interpretier- en. Dadurch kommt es zu Versionsnummern (z.B. VBE 1.02), die es bei der VESA natürlich nie gegeben hat... ;)
OEMStringPtr, Offset 006H, 16:16 Zeiger (RM, FAR):
Zeiger auf einen ASCIZ-String, der den Grafikchip und/oder die Produktfa- milie des Herstellers benennt (z.B. 'S3 Incorporated. Trio 64', 0).
HINWEIS: Leider hat die VESA das Stringformat nicht definiert. Es wird le- diglich eine maximale Länge von 255 Bytes empfohlen. Man muß also mit allem rechnen: ein- oder mehrzeilige Strings, Zeilenlänge bei jedem Hersteller anders, usw.
HINWEIS: Der Zeiger kann je nach VBE-Implementation ins RAM oder ein ROM zeigen. Erst ab VBE 2.0 wurde von der VESA definiert, daß der String bei einem Aufruf mit VBESignature = 'VBE2' zusätzlich auch in das OEMData-Feld kopiert werden muß. Dadurch ist im Protected Mode der Zugriff auf den String einfacher möglich.
Capabilities, Offset 00aH, 4 Bytes:
Obwohl vier Bytes zur Verfügung stehen, die etwas über die Unterstützung bestimmter spezifischer Features aussagen sollen, so sind doch bis jetzt lediglich drei Bit im ersten Byte (Offset 00aH) tatsächlich definiert:
Bit 0 = 0 DAC mit fixierter Datenbreite (6 Bit pro Primary Color)
--- VBE 1.2+ -----------------------------------------------------------
= 1 DAC kann auf 8 Bit pro Primary Color umgestellt werden
HINWEIS: Um eine definierte Ausgangsbasis zu bieten, wurde von der VESA festgelegt, daß breitenvariable DACs bei jedem Videomode Init auf 6BPPC zu setzen sind.
Um auf 8BPPC umzuschalten, dient seit VBE 1.2+ die Funktion 08H (AX = 4f08H, BX = 0800H).
--- VBE 2.0+ -----------------------------------------------------------
Bit 1 = 0 Controller ist VGA-kompatibel = 1 Controller ist NICHT VGA-kompatibel
HINWEIS: VGA-Kompatibilität wird von der VESA nur insofern definiert, daß alle Standard VGA-Modes, -Fonts und I/O-Ports unterstützt werden.
Das bedeutet aber nicht, daß alle Modes die gesetzt werden können, auch VGA-kompatible Speichermodelle benutzen, usw.
Bit 2 = 0 RAMDAC-Writes jederzeit möglich = 1 RAMDAC-Writes besser nur während Retraces
HINWEIS: Ein gesetztes Bit 2 bedeutet nicht, daß der RAMDAC nicht jederzeit beschrieben werden könnte, sondern daß er zum sogenannten "Schnee-Effekt" neigt, wenn das Beschreiben außerhalb eines Retraces geschieht.
Eine VBE-Funktion, um auf einen Retrace zu warten, gibt es erst seit VBE 2.0 (Funktion 4f09H, Bit 7 im BL-Register gesetzt), bei älteren VBEs muß der Pro- grammierer selbst Hand anlegen (siehe dazu auch das Input Status #1 Register, IS1R.ERA (3daH[0]), Topic BAW0019).
Bit 3..31 reserviert
VideoModePtr, Offset 00eH, 16:16 Zeiger (RM, FAR):
Dieser Zeiger zeigt auf eine Liste, welche alle Videomode-Nummern enthält, die von der VBE unterstützt werden. Die Liste enthält jeweils ein Word pro Modus und endet mit einer -1 (ffffH, EndOfList = EOL).
Grundsätzlich zu unterscheiden sind bei den Modusnummern drei Gruppen:
00..13H Standard VGA-Modes 14..7fH Extended OEM-Modes (Ausnahme: 6aH, VBE 800x600x16) 80..ffH reserviert 100H.. VBE Modes (siehe Funktion 4f02H, Set VBE Mode)
HINWEIS: Die Liste kann sich irgendwo im RAM oder ROM befinden. Erst ab VBE 2.0 ist definiert, daß sie bei Aufruf der Funktion 4f00H und einer VBESignature = 'VBE2' in das Reserved-Feld des VBEInfoBlock zu kopieren ist, oder sich zumindest statisch innerhalb der VBE- Implementation befinden muß.
HINWEIS: Bei S3-Chips ist es üblich, daß unmittelbar auf die EOL-Kennung eine weitere Liste mit S3-spezifischen Modusnummern folgt. Auch diese Liste endet wiederum mit einer EOL-Kennung von -1 (ffffH).
HINWEIS: Daß ein Modus laut Liste unterstützt wird, bedeutet leider noch lange nicht, daß er auch unbedingt verfügbar ist. Es könnte z.B. sein, daß ein Modus mehr RAM benötigt, als die SVGA on-board zur Verfügung hat, oder der Monitor bestimmte Modes bildtechnisch gar nicht leisten kann, usw.
Daher empfiehlt sich grundsätzlich ein Cross-Check über die Funk- tion 4f01H (Bit 0 in ModeInfoBlock.ModeAttributes muß = 1 sein), um sicherzustellen, daß ein Modus auch tatsächlich verfügbar ist.
HINWEIS: Beginnt die Liste direkt mit der EOL-Kennung -1 (ffffH) kommt man in den seltenen Genuß einer sogenannten ¯Stub-VBE®. Solche Stubs kommen in der Regel nur zum Einsatz, wenn nicht genug Platz vor- handen ist, um die komplette VBE unterzubringen. Stub-VBEs unter- stützen meistens nur die Funktion 4f00H in VBE-kompatibler Weise, die restlichen Funktionen sind rein herstellerspezifisch...
TotalMemory, Offset 012H, Word, ab VBE 1.1+:
Gesamtgröße des physikalisch installierten RAMs in Einheiten zu 64 KByte, also z.B. TotalMemory = 4 = 4 * 64 KByte = 256 KByte RAM.
HINWEIS: Nicht alle Videomodes können auch das gesamte verfügbare RAM nut- zen. Die Nutzbarkeit ist abhängig von dem Speichermodell bzw. dem Adressierungsverfahren, das der jeweilige Videomode verwendet. Im Videomode 13H z.B. werden immer nur die untersten 256 KByte adres- siert, auch wenn die Karte vielleicht 2 MByte oder mehr RAM hat.
OEMSoftwareRev, Offset 014H, Word, ab VBE 2.0+:
OEM-Versionsnummer der VBE als BCD. Genau wie es auch bei VBEVersion der Fall ist, trägt das Hi-Byte die Hauptversions- und das Lo-Byte die Unter- versionsnummer.
HINWEIS: Das Feld wird nur dann gefüllt, wenn die Funktion 4f00H mit einer VBESignature von 'VBE2' aufgerufen wird.
OEMVendorNamePtr, Offset 016H, 16:16 Zeiger (RM, FAR), ab VBE 2.0+:
Zeiger auf einen ASCIZ-String, der den Hersteller der VBE benennt.
HINWEIS: Das Feld wird nur dann gefüllt, wenn die Funktion 4f00H mit einer VBESignature von 'VBE2' aufgerufen wird.
HINWEIS: Die Strings, auf die OEMVendorNamePtr, OEMProductNamePtr und OEM- ProductRevPtr zeigen, dürfen laut VESA-Definition zusammen (inkl. ihrer Terminatoren) nie länger als 255 Bytes sein. Gedacht war diese Definition dazu, daß alle drei Strings im OEMData-Feld des VBEInfoBlock zurückgegeben werden können.
Genutzt wird das leider nicht von allen Herstellern, so daß man davon ausgehen muß, den String im Reserved-Feld, im OEMData-Feld oder irgendwo statisch innerhalb der VBE-Implementation anzutref- den... &)
OEMProductNamePtr, Offset 01aH, 16:16 Zeiger (RM, FAR), ab VBE 2.0+:
Zeiger auf einen ASCIZ-String, der den Chip, die Karte, o.ä. benennt.
HINWEIS: Es gelten die gleichen Hinweise wie für OEMVendorNamePtr (oben).
OEMProductRevPtr, Offset 01eH, 16:16 Zeiger (RM, FAR), ab VBE 2.0+:
Zeiger auf einen ASCIZ-String, der die Revisionsnummer des Chips, der Kar- te, des Treibers, o.ä. benennt.
HINWEIS: Es gelten die gleichen Hinweise wie für OEMVendorNamePtr (oben).
Reserved, Offset ???H, Länge ??? Bytes:
Dieses Feld wurde von der VESA dazu vorgesehen, die Liste der unterstüt- zten Videomode-Nummern aufzunehmen, falls eine solche Liste nicht statisch irgendwo innerhalb der VBE-Implementation verfügbar ist und daher bei Auf- ruf von Funktion 4f00H erst dynamisch erzeugt werden muß.
HINWEIS: Um es nochmal zu unterstreichen: das Feld kann, muß aber nicht, die Videomodus-Liste enthalten. Zudem sollte man das Feld nicht für andere Zwecke "mißbrauchen", da sich die VESA für zukünftige VBE-Versionen vorbehält, das Feld anderweitig zu nutzen... ;)
HINWEIS: Lage und Länge des Feldes sind von der VBE-Version abhängig:
VBE 1.0: Offset 12H, Länge 238 Bytes VBE 1.1: Offset 14H, Länge 236 Bytes VBE 1.2: Offset 14H, Länge 236 Bytes VBE 2.0: Offset 22H, Länge 222 Bytes
OEMData, Offset 100H, 256 Bytes, ab VBE 2.0+:
Dieses Feld wurde von der VESA als dynamischer Ablagepuffer zur Aufnahme der Strings OEMVendorName, OEMProductName und OEMProductRev definiert.
Leider halten sich nicht alle Hersteller bzw. VBE-Upgrader daran (SciTechs UniVBE 5.1a kopiert die Strings z.B. ins Reserved-Feld)... &)
Funktion 01H, Return VBE Mode Information -----------------------------------------
Parameter:
AX = 4f01H
CX = Nummer des Videomodes für den die Information angefordert wird.
HINWEIS: Es sollten nur Modusnummern verwendet werden, die auch tatsächlich gelistet sind (s. VBEInfoBlock.VideoModePtr).
Eine Übersicht aller definierten Nummern befindet sich im Parameterteil der Funktion 4f02H (Set VBE Mode).
ES:DI = Zeiger auf zu füllende ModeInfoBlock-Struktur
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
ES:DI = gefüllte ModeInfoBlock-Struktur (nur wenn AX = 004fH)
{ Struktur: ModeInfoBlock
Ofs Typ Bezeichnung
00H W ModeAttributes 02H B WinAAttributes 03H B WinBAttributes 04H W WinGranularity 06H W WinSize 08H W WinASegment 0aH W WinBSegment 0cH D WinFuncPtr 10H W BytesPerScanLine
--- bei VBE 1.0 und 1.1 nur optional, ab VBE 1.2+ zwingend ---------------
12H W XResolution 14H W YResolution 16H B XCharSize 17H B YCharSize 18H B NumberOfPlanes 19H B BitsPerPixel 1aH B NumberOfBanks 1bH B MemoryModel 1cH B BankSize
--- VBE 1.0 --------------------------------------------------------------
1dH 227 B Reserved
--- VBE 1.1 --------------------------------------------------------------
1dH B NumberOfImagePages 1eH 226 B Reserved
--- VBE 1.2 --------------------------------------------------------------
1dH B NumberOfImagePages 1eH B Reserved 1fH B RedMaskSize 20H B RedFieldPosition 21H B GreenMaskSize 22H B GreenFieldPosition 23H B BlueMaskSize 24H B BlueFieldPosition 25H B RsvdMaskSize 26H B RsvdFieldPosition 27H B DirectColorModeInfo 28H 216 B Reserved
--- VBE 2.0+ -------------------------------------------------------------
1dH B NumberOfImagePages 1eH B Reserved 1fH B RedMaskSize 20H B RedFieldPosition 21H B GreenMaskSize 22H B GreenFieldPosition 23H B BlueMaskSize 24H B BlueFieldPosition 25H B RsvdMaskSize 26H B RsvdFieldPosition 27H B DirectColorModeInfo 28H D PhysBasePtr 2cH D OffScreenMemOffset 30H W OffScreenMemSize 32H 206 B Reserved
}
ModeAttributes, Offset 00H, Word:
Dieses Feld beschreibt einige wichtige Charakteristika des Videomodes. Ob- wohl eigentlich ein Word, ist bis dato jedoch nur das Lo-Byte definiert:
Bit 0 = 0 Videomodus ist NICHT verfügbar = 1 Videomodus ist verfügbar und kann benutzt werden
HINWEIS: Nur anhand dieses Bits kann festgestellt werden, ob ein Modus, der lt. Funktion 4f00H von der Karte un- terstützt wird (s. VBEInfoBlock.VideoModePtr), auch tatsächlich verfügbar ist.
Es könnte ja z.B. sein, daß ein Modus mehr RAM be- nötigt, als die SVGA on-board hat, der Monitor den Modus gar nicht darstellen kann, usw., ... ;)
Bit 1 = 0 Felder ab XResolution (Offset 12H) sind undefiniert = 1 Felder ab XResolution (Offset 12H) wurden gefüllt
HINWEIS: Mit VBE 1.2 legte die VESA die Füllung der Felder ab XResolution als zwingend erforderlich fest, da- her kann man davon ausgehen, daß ab VBE 1.2+ dieses Bit immer gesetzt ist.
Bis einschließlich VBE 1.1 dagegen hing es alleine vom Hersteller ab, ob gefüllt wurde oder nicht.
Bit 2 = 0 TTY-Ausgabefunktionen werden NICHT vom BIOS unterstützt = 1 TTY-Ausgabefunktionen werden vom BIOS unterstützt
HINWEIS: Ist dieses Bit gesetzt, kann der Programmierer da- von ausgehen, daß in diesem Videomodus nachfolgende TTY-Funktionen vom BIOS unterstützt werden:
INT 10.01H Set Cursor Shape INT 10.02H Set Cursor Position INT 10.06H Scroll Window up (or blank) INT 10.07H Scroll Window down (or blank) INT 10.09H Write Char & Attr at Cursor Position INT 10.0aH Write Char Only at Cursor Position INT 10.0eH TTY-Output of Char
Bit 3 = 0 Dieser Videomodus ist ein Monochrommodus = 1 Dieser Videomodus ist ein Farbmodus
Bit 4 = 0 Dieser Videomodus ist ein Textmodus = 1 Dieser Videomodus ist ein Grafikmodus
--- VBE 2.0+ -----------------------------------------------------------
Bit 5 = 0 Dieser Videomodus ist VGA-kompatibel = 1 Dieser Videomodus ist NICHT VGA-kompatibel
HINWEIS: Ist dieses Bit gelöscht (!), kann der Programmierer davon ausgehen, daß der Modus über die Standard VGA I/O-Ports programmierbar und das Video-RAM über die Video-Segmente WinASegment und/oder WinBSegment an- sprechbar ist (soweit definiert).
Bit 6 = 0 Frame Buffer in WinASegment und/oder WinBSegment mappbar = 1 nicht mappbar (kein Windowing möglich)
Bit 7 = 0 Linear Frame Buffer Speichermodell NICHT verfügbar = 1 Linear Frame Buffer Speichermodell verfügbar
HINWEIS: Das Linear Frame Buffer Model (LFBM) gestattet in vielen Fällen eine wesentlich höhere Performance als das Windowed Frame Buffering.
Um ein LFBM zu aktivieren, muß beim Setzen des Vi- deomodes über Funktion 4f02H Bit 14 im BX-Register gesetzt sein. Da (speziell im Protected Mode) aber noch diverse andere Sachen zu beachten sind, ist das LFBM in Topic BAW0022 gesondert dokumentiert.
Bit 8..15 reserviert
WinAAttributes bzw. WinBAttributes, Offset 02H bzw. 03H, Byte:
Diese Bytes beschreiben die Windowing-Charakteristik für das entsprechende Window (A bzw. B). Die Bitdefinition ist bei beiden gleich:
Bit 0 = 0 Window NICHT existent = 1 Window existiert
Bit 1 = 0 Window ist NICHT lesbar = 1 Window ist lesbar
Bit 2 = 0 Window ist NICHT beschreibbar = 1 Window ist beschreibbar
Bits 3..7 reserviert
HINWEIS: Sollte Windowing in einem Mode nicht unterstützt werden (Bit 0 in WinAAtributes UND WinBAttributes gelöscht), kann der Programmier- er davon ausgehen, daß sich das Video-RAM an der für das verwen- dete MemoryModel üblichen Segmentadresse befindet (z.B. a000H).
WinGranularity, Offset 04H, Word:
Dieses Feld gibt Auskunft darüber, in welchen KByte-Einheiten ein Window im Video-RAM verschoben werden kann. Diese Angabe wird z.B. benötigt, um über Funktion 4f05H (BX = 000?H, DX in Einheit WinGranularity) die Start- adresse eines Fensters im Video-RAM setzen zu können. Ist WinGranularity z.B. = 4, ist die Startadresse immer nur in 4KByte-Schritten verschiebbar.
HINWEIS: Der Inhalt dieses Feldes gilt für das jeweilige Window nur dann als definiert, wenn dessen Bit 0 in Win?Attributes gesetzt ist, d.h. nur dann, wenn das Fenster auch existiert... &)
WinSize, Offset 06H, Word:
WinSize spezifiziert die Größe eines Windows im Video-RAM in KBytes.
WinASegment bzw. WinBSegment, Offset 08H bzw. 0aH, Word:
Diese Felder geben an, an welcher Segmentadresse das jeweilige Window im CPU-Adressraum für den Zugriff eingeblendet wird.
WinFuncPtr, Offset 0cH, 16:16 Zeiger (RM, FAR):
Um zwischen mehreren Bildschirmseiten umzuschalten (Pageflips), bzw. die Startadresse eines Windows verändern, sind eigentlich die Funktionen 4f05H bzw. 4f07H der VBE zuständig (Display Window Control bzw. Set/Get Display Start).
Diese Funktionen werden mit zunehmender Aufruffrequenz aber logischerweise immer unbrauchbarer, da Softwareinterrupts bekanntlich nicht gerade zu den schnellsten Aufrufarten von Funktionen gehören.
Dessen sind sich natürlich auch die Hersteller bewußt ;), also bieten die meisten von ihnen dem Programmierer schnellere Alternativen in Form einer direkten Einsprungadresse zur Funktion 4f05H an, welche dann in WinFuncPtr zu finden ist. Für Funktion 4f07H existiert leider nichts vergleichbares, zumindest nicht, soweit es den Real Address Mode betrifft... 8(
Anders im Protected Mode, da zaubert die PM-API (siehe Funktion 4f0aH) dem Programmierer wenigstens einen kleinen Silberstreif an den Horizont... ;)
Wie dem auch sei, ist WinFuncPtr ungleich 0000:0000H, kann Funktion 4f05H also direkt, d.h. per FAR CALL aufgerufen werden. Die Parameter sind dabei die gleichen, wie sie auch beim Interruptaufruf von INT 10.4f05H benutzt würden, mit einer Ausnahme: das AX-Register kann enthalten, was es will, es muß nicht zwingend 4f05H sein.
Zu beachten ist hier eigentlich nur, daß bei FAR CALLs bis einschließlich VBE 1.2 kein Status im AX-Register zurückgegeben wird (bei VBE 2.0 schon), der Inhalt von AX aber trotzdem zerstört wird. Der Inhalt des DX-Registers wird ebenfalls zerstört (auch bei BH = 00H, Set Memory Window!), das gilt aber allerdings für alle VBE-Versionen.
Es dürfte einleuchten, daß der FAR CALL zwar bereits schneller als ein INT ist, aber bei weitem nicht die schnellste Methode darstellt. Sollte die VBE im ROM implementiert sein, würde eine Verlagerung ins RAM für weitere Beschleunigung sorgen (z.B. per Shadow RAM oder durch Kopieren/Relozieren der gesamten VBE ins RAM)...
Die absolute Top-Performance erhält man aber natürlich wie immer nur durch direkte Low-Level Programmierung der SVGA Ports und Register. Allerdings halst man sich damit auch entsprechend aufwendige und umfangreiche Chip- Detection/-Support Routinen auf. Halt eine reine Aufwand/Nutzenfrage... ;)
HINWEIS: Die Funktion 4f05H ist nur für Windowed Frame Buffering gedacht, egal ob nun per INT oder via FAR CALL aufgerufen. Im Linear Frame Buffering Mode erntet man hier höchstens einen Fehlercode 03H in AH (zumindest bei VBE 2.0)... ;)
HINWEIS: Es dürfte klar sein, daß unterschiedliche Videomodes auch unter- schiedliche direkte Einsprungadressen besitzen können, oder? &)
BytesPerScanLine, Offset 10H, Word:
Anzahl volle Bytes pro logischer Bildschirmzeile im Video-RAM. Vorsicht, logische Bildschirmzeilen können genauso lang, aber natürlich auch länger als dargestellte Bildschirmzeilen sein... B)
XResolution, Offset 12H, Word:
Breite des Videomode in Pixeln (Grafikmodi) bzw. CCUs (Zeichen, Textmodi).
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
YResolution, Offset 14H, Word:
Höhe des Videomode in Pixeln (Grafikmodi) bzw. Zeichen (Textmodi).
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
XCharSize, Offset 16H, Byte:
Matrixbreite pro Zeichen in Pixeln (1..).
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
YCharSize, Offset 17H, Byte:
Matrixhöhe pro Zeichen in Pixeln (1..).
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
NumberOfPlanes, Offset 18H, Byte:
Dieses Feld gibt Auskunft darüber, mit wievielen Speicherplanes in diesem Videomodus gearbeitet wird (1..). Bei 16-farbigen Standard VGA-Grafikmodes wäre NumberOfPlanes beispielsweise = 4, bei Packed Pixel Modes = 1, usw...
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
BitsPerPixel, Offset 19H, Byte:
Dieses Feld gibt an, aus wievielen Bits jedes Pixel besteht.
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
NumberOfBanks, Offset 1aH, Byte:
Anzahl Speicherbänke, in die die Bildschirmzeilen gruppiert sind.
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
MemoryModel, Offset 1bH, Byte:
Vom Videomodus benutztes Speichermodell:
00H Text 01H CGA-Grafik 02H Hercules-Grafik 03H planar (4-plane) 04H packed Pixel 05H Non-Chain4, 256 Farben 06H Direct Color (VBE 1.2+) 07H YUV/YIQ (VBE 1.2+) 08..0fH reserviert für VESA 10..ffH reserviert für OEMs
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
HINWEIS: Direct Color Modes werden erst seit VBE 1.2+ als MemoryModel 06H in Verbindung mit den *MaskSize- und *FieldPosition-Feldern defi- niert.
Bis einschließlich VBE 1.1 waren sie dagegen als MemoryModel 04H (packed Pixel) mit entsprechenden BitsPerPixel-Werten deklariert (1:5:5:5 = 16, 8:8:8 = 24, 8:8:8:8 = 32).
BankSize, Offset 1cH, Byte:
Spezifiziert die Größe pro Speicherbank in Einheiten zu 1 KByte.
HINWEIS: Bis einschließlich VBE 1.1 ist der Inhalt dieses Feldes nur gül- tig, wenn Bit 1 in ModeInfoBlock.ModeAttributes gesetzt ist.
NumberOfImagePages, Offset 1dH, Byte, ab VBE 1.1+:
Gibt die maximale Anzahl Bildschirmseiten (abzüglich 1) an, die in diesem Videomodus zur Verfügung stehen. Nur wenn dieser Wert größer als Null ist, ist die Hauptvoraussetzung für Pageflips bzw. Änderungen der Window-Start- adresse gegeben.
Red/Green/Blue/RsvdMaskSize, Offset 1f/21/23/25H, Byte, ab VBE 1.2+:
Diese Felder definieren die Bitbreiten der jeweiligen Farbkomponenten, aus denen ein Direct Color Pixel zusammengesetzt wird. Die Verteilung bei ei- nem YUV MemoryModel ist so definiert: Y = Green, U = Blue, V = Red.
Bei einem 5:4:3 Modus (ähm, nur Beispielcharakter ;)) hätten wir also:
RedMaskSize = 5 GreenMaskSize = 4 BlueMaskSize = 3 RsvdMaskSize = 0
Red/Green/Blue/RsvdFieldPosition, Offset 20/22/24/26H, Byte, ab VBE 1.2+:
Dieser Felder definieren die Position des niederwertigsten Bits (LSB) der jeweiligen Farbkomponente innerhalb des Direct Color bzw. YUV-Pixels.
Gleiches Beispiel wie zuvor (nicht existenter 5:4:3 Modus):
RedFieldPosition = 7 GreenFieldPosition = 3 BlueFieldPosition = 0 RsvdFieldPosition = 0
DirectColorModeInfo, Offset 27H, Byte, ab VBE 1.2+:
Dieses Byte enthält noch zwei weitere Zusatzinformationen für Direct Color Modes:
Bit 0 = 0 Color Ramp fest vorgegeben 1 Color Ramp programmierbar
HINWEIS: Ist die Color Ramp programmierbar (Bit 0 = 1), kann der Programmierer voraussetzen, daß die Palette über INT 10.1012H, bzw. ab VBE 2.0 auch über INT 10.4f09H mit anderen RGB-Farbwerten versehen werden kann.
Bit 1 = 0 Bits im Rsvd-Bereich des Direct Color Pixels sind reserviert 1 Bits im Rsvd-Bereich des Direct Color Pixels "mißbrauchbar" ;)
Bits 2..7 reserviert
PhysBasePtr, Offset 28H, DWord, ab VBE 2.0+:
Handelt es sich bei diesem Videomodus um einen Modus, der im Linear Frame Buffering Mode betrieben werden kann (ModeInfoBlock.ModeAttributes Bit 7 = 1), kann man PhysBasePtr die physikalische Adresse (32bit linear) des LFB entnehmen (näheres zum LFBM siehe Topic BAW0022), sonst ist PhysBasePtr 0.
OffScreenMemOffset, Offset 2cH, DWord, ab VBE 2.0+:
Sollte die SVGA über OffScreen-Memory verfügen (s. OffScreenMemSize), gibt dieser 32bit-Offset dessen Beginn relativ zu PhysBasePtr an.
OffScreenMemSize, Offset 30H, Word, ab VBE 2.0+:
Spezifiziert (soweit vorhanden) die Größe verfügbaren und kontinuierlichen OffScreen-Memorys in 1 KByte-Einheiten.
Funktion 02H, Set VBE Mode --------------------------
Parameter:
AX = 4f02H
BX = Nummer des gewünschten Videomodes
Bit(s)
0...8 Modusnummer
00..13H Standard VGA-Modes 14..69H Extended OEM-Modes 6aH VBE 800x600x16 6b..7fH Extended OEM-Modes 80..ffH reserviert 100H VBE 640x400x256 101H VBE 640x480x256 102H VBE 800x600x16 103H VBE 800x600x256 104H VBE 1024x768x16 105H VBE 1024x768x256 106H VBE 1280x1024x16 107H VBE 1280x1024x256
--- VBE 1.1+ ----------------------------------------
108H VBE 80x60 Text 109H VBE 132x25 Text 10aH VBE 132x43 Text 10bH VBE 132x50 Text 10cH VBE 132x60 Text
--- VBE 1.2+ ----------------------------------------
10dH VBE 320x200x32K (1:5:5:5) 10eH VBE 320x200x64K (5:6:5) 10fH VBE 320x200x16.8M (8:8:8) 110H VBE 640x480x32K (1:5:5:5) 111H VBE 640x480x64K (5:6:5) 112H VBE 640x480x16.8M (8:8:8) 113H VBE 800x600x32K (1:5:5:5) 114H VBE 800x600x64K (5:6:5) 115H VBE 800x600x16.8M (8:8:8) 116H VBE 1024x768x32K (1:5:5:5) 117H VBE 1024x768x64K (5:6:5) 118H VBE 1024x768x16.8M (8:8:8) 119H VBE 1280x1024x32K (1:5:5:5) 11aH VBE 1280x1024x64K (5:6:5) 11bH VBE 1280x1024x16.8M (8:8:8)
--- VBE 2.0+ ----------------------------------------
120H VBE 1600x1200x256 121H VBE 1600x1200x32K (1:5:5:5) 122H VBE 1600x1200x64K (5:6:5)
81ffH Special Preserve Mode
9..13 reserviert (0)
14 = 0 Windowed Frame Buffer Model (WFBM) benutzen = 1 Linear Frame Buffer Model (LFBM) benutzen
HINWEIS: Das LFBM ist erst ab VBE 2.0+ definiert, d.h. bis einschließlich VBE 1.2 galt das Bit 14 immer als reserviert (0).
15 = 0 Video-RAM beim Init löschen = 1 Video-RAM beim Init NICHT löschen
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
HINWEIS: Man sollte davon absehen, Videomodes über Funktion 4f02H zu setzen, die nicht in der Liste der unterstützten Videomodes (s. dazu Funk- tion 4f00H, VBEInfoBlock.VideoModePtr) auftauchen... ;)
HINWEIS: Ebenso sinnlos ist latürnich das Setzen von Bit 14 zur Aktivierung des LFBMs, wenn laut Funktion 4f01H das LFBM gar nicht unterstützt wird (Bit 7 in ModeInfoBlock.ModeAttributes = 0) oder .PhysBasePtr = 00000000H ist... >-)
HINWEIS: Es mag sein, daß die SVGA auch das Setzen von Standard VGA-Modes (00..13H) bzw. Extended OEM-Modes (14..69H bzw. 6b..7fH) über die Funktion 4f02H zulässt (falls in der Liste). Beim Setzen von Video- modi dieses Typs auf herkömmlichem Weg (INT 10.00??H), kann man be- kanntlich durch ein gesetztes Bit 7 verhindern, daß das Video-RAM beim Init gelöscht wird.
Bei der VBE-Funktion 4f02H funktioniert dies dagegen nicht. Ein ge- setztes Bit 7 würde den Funktionsaufruf scheitern lassen. Statt des Bits 7 ist hier ersatzweise Bit 15 zu benutzen (gleiche Funktion)..
HINWEIS: Ist Bit 15 beim Aufruf von Funktion 4f02H gelöscht, werden alle ge- meldeten Bildschirmseiten (lt. ModeInfoBlock.NumberOfImagePages) in Grafikmodi mit 00H-Bytes (schwarz) und in Textmodi mit 0720H-Words (hellgraue Leerzeichen) gefüllt.
HINWEIS: Mit der VBE 2.0 hat die VESA leider auch den Startschuss dazu gege- ben, daß uns die Chip-Hersteller wieder mit einem Inkompatibiltäts- Chaos belegen können. Es werden zukünftig weder neue standardisier- te VBE Videomodusnummern für bestimmte Auflösungen/Farbtiefen ver- geben, noch werden alte weiter unterstützt... >*(
Ergo bleibt einem nur der umständliche Umweg, alle Videomodes der Liste (Funktion 4f00H, VBEInfoBlock.VideoModePtr) mit der Funktion 4f01H abzufragen, und den ModeInfoBlock manuell nach der gewünsch- ten Auflösung, Farbtiefe, usw., durchzuscannen... 8(*)
HINWEIS: Der Special Preserve Mode (81ffH, ab VBE 2.0+) ist ein Modus, der den aktuellen Inhalt des gesamten SVGA-RAMs rettet und gleichzeitig vollen Zugriff auf dieses RAM gestattet.
Dieser Modus dürfte hauptsächlich bei der Umsetzung von Power Mana- gements (Energiesparmodi) Verwendung finden. Er kann aber unter Um- ständen auch bei diversen Tricksereien ;) oder auch der Fehlersuche hilfreich sein, da nicht in allen Videomodi auch immer der Zugriff auf das gesamte SVGA-RAM zulässig ist.
Die VESA empfiehlt zwar den Herstellern, für einen Special Preserve Mode ein Speichermodell vom Typ Packed-Pixel zu verwenden, aber wie immer ;) gibts auch hier keine Garantie. Am besten schaut man sich halt den ModeInfoBlock zum Modus 81ffH an, um herauszubekommen, wie der jeweilige Hersteller tatsächlich verfährt... ;)
Funktion 03H, Return current VBE Mode -------------------------------------
Parameter:
AX = 4f03H
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
BX = Informationen zum aktuellen Videomode (nur wenn AX = 004fH)
Bit(s)
0...8 Modusnummer (siehe Liste bei Funktion 4f02H)
9..13 reserviert (0)
14 = 0 Windowed Frame Buffer Model (WFBM) aktiv = 1 Linear Frame Buffer Model (LFBM) aktiv (VBE 2.0+)
15 = 0 Video-RAM wurde beim Init gelöscht = 1 Video-RAM wurde beim Init NICHT gelöscht
HINWEIS: Es dürfte einleuchten, daß diese Funktion nur Vidomodes korrekt zu- rückgeben kann, die auch über Funktion 4f02H gesetzt wurden. Wurde der Modus dagegen über INT 10.00??H gesetzt, gibt es keine Garantie für die Richtigkeit des Rückgabeparameters im BX-Register... &)
Funktion 04H, Save/Restore State --------------------------------
Parameter:
AX = 4f04H
DL = 00H Benötigte Puffergröße für spezifizierte CX-Stati ermitteln 01H Spezifizierte CX-Stati in Puffer bei ES:BX sichern 02H Spezifizierte CX-Stati aus Puffer bei ES:BX restaurieren
CX = gewünschte Stati für Ermittlung/Sicherung/Restauration:
Bit
0 = 1 Status der Video Hardware 1 = 1 Status des BIOS-Datasegments 2 = 1 Status des DAC 3 = 1 Status der SVGA
ES:BX = Zeiger auf Sicherungs- bzw. Restaurationspuffer (nur wenn DL <> 0)
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
BX = Anzahl benötigte 64-Byte-Blocks (nur Funktion DL = 00H)
ES:BX = gefüllter Puffer (nur Funktion DL = 01H)
HINWEIS: Beim Sichern werden die angeforderten Daten normalerweise NIE am Beginn des Puffers abgelegt. Stattdessen trägt die VBE-Implementation in den ersten vier Words ab ES:BX für jeden laut CX angeforderten Statustyp eine entsprechende Offsetadresse (ES-relativ) ein:
Ofs Typ Bezeichnung
00H W Offset für Video Hardware Status 02H W Offset für BIOS-Datasegment Status 04H W Offset für DAC Status 06H W Offset für SVGA Status
Die Bytes ab ES:BX+08H bis vor den lt. Offsets ersten ei- gentlichen Statuspuffer, sowie eventuelle Lücken zwischen zwei Statuspuffern, sind undefiniert.
Standardpuffer für Video Hardware Status (CX: Bit 0 = 1):
Ofs Typ Bezeichnung
00H B Inhalt des TS-Indexregisters (Port 3c4H) 01H B Inhalt des CRTC-Indexregister (Port 3d4H) 02H B Inhalt des GDC-Indexregisters (Port 3ceH) 03H B Inhalt des ATC-Indexregister (Port 3c1H) 04H B Inhalt des FCR-Registers (Port 3caH) 05H 4 B Inhalt der TS-Register 01..04H 09H B Inhalt des MOR-Registers (Port 3ccH) 0aH 25 B Inhalt der CRTC-Register 00..18H 23H 21 B Inhalt der ATC-Register 00..13H 37H 9 B Inhalt der GDC-Register 00..08H 40H W CRTC-Basisadresse (i.d.R. 03d4H) 42H B Inhalt von Latch 0 43H B Inhalt von Latch 1 44H B Inhalt von Latch 2 45H B Inhalt von Latch 3
Standardpuffer für BIOS-Datasegment (CX: Bit 1 = 1):
00H B Inhalt von 0040:0049H : : 5fH B Inhalt von 0040:00a8H
Standardpuffer für DAC Status (CX: Bit 2 = 1):
Ofs Typ Bezeichnung
00H B Inhalt des DAC State Port (3c7H) 01H B Inhalt des Pixel Write Address Port (3c8H) 02H B Inhalt des Pixel Mask Port (3c6H) 03H 768 B Inhalt der RGB-Triple 0..255 303H B Inhalt des ATC-Registers 14H
Standardpuffer für SVGA Status (CX: Bit 3 = 1):
Na? Naaaaah? Genau: jibbet nich', ist herstellerspezifisch... ;)
HINWEIS: Bei Funktion 4f04H handelt es sich im Prinzip nur um eine Erweiter- ung der entsprechenden VGA-Funktion INT 10.1c??H. Diese Erweiterung war erforderlich, da SVGAs ja über Register verfügen, die nicht zum VGA-Standard gehören und es daher über INT 10.1c??H nicht möglich gewesen wäre, den kompletten SVGA-Hardwarestatus (exkl. Video-RAM) zu sichern bzw. ihn wieder zu restaurieren.
Funktion 05H, Display Window Control ------------------------------------
Parameter:
AX = 4f05H
BH = 00H Startadresse eines Windows setzen (WinGranularity-Units!) 01H Startadresse eines Windows ermitteln (WinGranularity-Units!)
BL = 00H Window A setzen/ermitteln = 01H Window B setzen/ermitteln
DX = Neue Startadresse für Window im Video-RAM (nur Funktion BH = 00H)
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
DX = Aktuelle Startadresse (nur Funktion BH = 01H)
HINWEIS: Die zu setzende bzw. zu ermittelnde Startadresse wird in der Ein- heit WinGranularity (siehe Funktion 4f01H, ModeInfoBlock) erwartet bzw. zurückgegeben und ist naturgemäß etwas grob. Für kleinere Ein- heiten ist daher eher Funktion 4f07H empfehlenswert... ;)
HINWEIS: Ist (speziell im Real Address Mode) Performance gefragt, sollte man imo besser erst einmal prüfen, ob der Hersteller den Programmierer eventuell mit einem direkten Einsprungpunkt für diese Funktion un- terstützt (siehe Funktion 4f01H, ModeInfoBlock.WinFuncPtr).
HINWEIS: Im Protected Mode ab VBE 2.0 sollte man imo sowohl INT, als auch FAR CALL Variante von Funktion 4f05H gänzlich ignorieren, und sich stattdessen der wesentlich schnelleren PM-API (Funktion 4f0aH) be- dienen. Kein Thunking = mehr Speed... &)
HINWEIS: Die Funktion 4f05H ist nur für Windowed Frame Buffering gedacht, egal ob nun per INT oder via FAR CALL aufgerufen. Im Linear Frame Buffering Mode erntet man hier höchstens einen Fehlercode 03H in AH (zumindest bei VBE 2.0)... ;)
Funktion 06H, Set/Get Logical Scan Line Length (VBE 1.1+) ---------------------------------------------------------
Parameter:
AX = 4f06H
BL = 00H Länge der Scan Line in Pixeln setzen 01H Länge der Scan Line ermitteln
--- VBE 2.0+ -----------------------------------------------------
02H Länge der Scan Line in Bytes setzen 03H Maximallänge der Scan Line ermitteln
CX = gewünschte Länge in Pixeln (BL = 00H) bzw. Bytes (BL = 02H)
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
BX = Bytes pro Scan Line
CX = Pixel pro Scan Line
DX = Maximalanzahl nutzbarer Scan Lines (nur Funktionen BL <> 03H)
HINWEIS: Welche Scan Line Längen (egal ob in Pixeln oder Bytes) gesetzt wer- den können, ist absolut hardwarespezifisch. Unterstützt die Hard- ware, aus welchen Gründen auch immer, die angeforderte Länge nicht exakt mit dem übergebenen Wert, so soll die VBE die Scan Line Länge auf den nächstgrößeren Wert setzen, der technisch noch möglich ist, oder ggf. mit Fehlerstatus aus der Funktion zurückkehren.
Um zu überprüfen, was die VBE aus dem gewünschten Wert tatsächlich gemacht hat, empfiehlt sich daher imo immer eine Gegenkontrolle mit dem zurückgegebenen CX-Wert.
HINWEIS: Für die erweiterten Textmodes (VBE 108..10cH) gilt der vorstehende Hinweis natürlich ebenfalls. Wer sich Ärger ersparen möchte, sollte sich daher imo angewöhnen, in erweiterten Textmodi immer nur Viel- fache von ModeInfoBlock.XCharSize (siehe Funktion 4f01H) zu setzen.
HINWEIS: Es soll ein paar SVGAs geben, die beim Setzen technisch zu großer Scan Line Längen OK-Status (AX = 004fH) zurückgeben, obwohl die an- geforderte Länge nicht unterstützt wird bzw. gesetzt werden konnte.
Workarounds für solche Fälle kann ich nicht bieten. Fragt halt den entsprechenden Hersteller, welchen Bock er da geschossen hat... ;)
HINWEIS: Bei Einsatz der Funktion BL = 03H erhält man in BX und CX immer den jeweils kleineren von zwei evtl. verschiedenen Werten zurück. Dabei handelt es sich entweder um die maximale Zeilenlänge, die technisch überhaupt darstellbar wäre, oder die maximale Zeilenlänge, bei der im momentanen Videomodus auch noch alle Zeilen unterstützt würden.
HINWEIS: Laut offizieller VBE 2.0 Dokumentation soll auch nach Rückkehr aus Funktion BL = 03H das DX-Register mit der maximalen Anzahl der Scan Lines gefüllt sein.
Mir ist seit Einführung von VBE 2.0 bisher noch keine einzige SVGA untergekommen, bei der das tatsächlich der Fall gewesen wäre. Macht Euch selbst Euren Reim drauf... ;)
Funktion 07H, Set/Get Display Start (VBE 1.1+) ----------------------------------------------
Parameter:
AX = 4f07H
BL = 00H Display Start setzen (unmittelbar) 01H Display Start ermitteln
--- VBE 2.0+ -----------------------------------------------------
80H Display Start setzen (während Vertical Retrace)
BH = 00H zwingend vorgegeben (reserviert)
--- nur Funktionen BL <> 01H ---------------------------------------------
CX = erstes darzustellendes Pixel (X-Pos der oberen linken Ecke)
DX = erste darzustellende Zeile (Y-Pos der oberen linken Ecke)
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
--- nur Funktion BL = 01H ------------------------------------------------
CX = erstes dargestelltes Pixel (X-Pos der oberen linken Ecke)
DX = erste dargestellte Zeile (Y-Pos der oberen linken Ecke)
HINWEIS: Diese Funktion ist sozusagen die feinere Variante von 4f05H, d.h. sie sollte also eigentlich prädestiniert für Smooth Scrolling sein.
Ist sie aber für horizontales Scrolling unglücklicherweise, je nach Hersteller und/oder Videomodus, nicht immer... >-(
Es kann also durchaus vorkommen, daß in einem Videomodus die einen Chip-Hersteller pixelweises Scrolling zulassen, andere aber nur Be- wegungen in 2er-, 4er- oder weiß-der-Geier-Schrittweiten über 4f07H anbieten. Für die Zwischenschritte darf sich der Programmierer dann wie üblich in Low-Level Interna stürzen... ;)
Da heutzutage aber eh meist mit Double- oder Triple Buffering gear- beitet wird und das Smooth Scrolling häufig bereits im System-RAM stattfindet, bevor ins SVGA-RAM geblastet wird, dürfte diese kleine Unpässlichkeit leicht zu verschmerzen sein... &)
HINWEIS: Sollten für das Setzen des Display Starts unzulässige Werte angege- ben werden (unzulässig insofern, daß die Koordinate RAM-WrapAround verursachen würde bzw. keine ganze Seite dargestellt werden könnte) soll die VBE laut VESA-Empfehlung gar nichts machen und einfach nur mit Fehlerstatus zurückkehren. Leider nur eine Empfehlung und keine Definition, also rechnet lieber mit Eigensinnigkeiten... &)
HINWEIS: Im Protected Mode sollte man imo auf den Einsatz von Funktion 4f07H verzichten und stattdessen aus Performancegründen lieber die PM-API (siehe Funktion 4f0aH) einsetzen.
Funktion 08H, Set/Get DAC Palette Format (VBE 1.2+) ---------------------------------------------------
Parameter:
AX = 4f08H
BL = 00H Bits per Primary Color setzen 01H Bits per Primary Color ermitteln
--- nur Funktion BL = 00H ------------------------------------------------
BH = gewünschte Anzahl Bits per Primary Color (BPPC)
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen
--- VBE 2.0+ -----------------------------------------------------
02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
BH = aktuelle Anzahl Bits per Primary Color
HINWEIS: Diese Funktion ist nur für Speichermodelle 00H bis 05H (siehe dazu Funktion 4f01H, ModeInfoBlock.MemoryModel) gedacht. Ein Aufruf in einem Videomodus mit Direct Color oder YUV-Speichermodell lässt die Funktion mit Fehlerstatus scheitern.
HINWEIS: Um festzustellen, ob man es denn überhaupt mit einem umschaltbaren DAC zu tun hat, kann man Bit 0 in VBEInfoBlock.Capabilities (siehe dazu auch Funktion 4f00H) prüfen. Ist es gesetzt, ist das der Fall.
HINWEIS: Um eine definierte Ausgangsbasis zu bieten legte die VESA fest, daß breitenvariable DACs bei jedem Videomode Init auf 6BPPC zu setzen sind (Standard-VGA).
HINWEIS: Wird eine gewünschte Anzahl Bits beim Setzen der BPPC von der Hard- ware nicht unterstützt, korrigiert die VBE-Implementation den Wert automatisch auf den hardwaretechnisch nächsttieferen. Im Zweifels- fall empfiehlt sich folglich eine Überprüfung des BH-Registers nach der Rückkehr aus der Funktion.
Funktion 09H, Set/Get Palette Data (VBE 2.0+) ---------------------------------------------
Parameter:
AX = 4f09H
BL = 00H Primary Palette setzen (unmittelbar) 01H Primary Palette ermitteln 02H Secondary Palette setzen 03H Secondary Palette ermitteln 80H Primary Palette setzen (während VRetrace)
CX = Anzahl zu setzende/ermittelnde Palettenregister für Aktion [BL]
DX = Nummer des ersten Palettenregisters für Aktion [BL]
ES:DI = Zeiger auf zu benutzenden Palettenpuffer.
HINWEIS: Das Palettenformat ist KEIN normaler RGB-Standard, d.h. die Farbe eines Registers wird nicht alleine durch drei RGB-Bytes definiert, sondern muß zusätzlich ein folgendes Reserved-Byte enthalten (auch in 256-Farbmodi!).
Mit anderen Worten also: Red, Green, Blue, Rsvd, Red, ...
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen 02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
ES:DI = gefüllter Puffer (ermittelte Palette, nur BL = 01H oder 03H)
HINWEIS: Um eine definierte Ausgangsbasis zu bieten legte die VESA fest, daß breitenvariable DACs bei jedem Videomode Init auf 6BPPC zu setzen sind (Standard-VGA).
Hat man es mit einem breitenvariablen RAMDAC zu tun (erkennbar an gesetztem Bit 0 in VBEInfoBlock.Capabilities, s. Funktion 4f00H), ist der Einsatz von Funktion 4f08H (Set/Get DAC Palette Format) vor einer Palettenbearbeitung meist von großem Nutzen, sei es nun, um das Palettenformat vorab festzustellen oder es festzulegen.
HINWEIS: Vor dem Setzen einer primären Palette empfiehlt sich eine einmalige Prüfung, ob es sich beim angesprochenen RAMDAC evtl. um einen han- delt, der noch zum "Snowing" neigt, falls er außerhalb von Retraces beschrieben wird.
Ist das der Fall (gesetztes Bit 2 in VBEInfoBlock.Capabilities, s. Funktion 4f00H), kann man statt BL = 00H z.B. BL = 80H benutzen, um dem "Snowing" zu entgehen.
HINWEIS: Das Vorhandensein einer Secondary Palette kann nicht vorausgesetzt werden, sondern ist rein implementationsspezifisch. Kommt die Funk- tion mit Fehlerstatus AH = 02H zurück, gibt's eben keine Secondary Palette... ;)
Funktion 0aH, Return VBE Protected Mode Interface (VBE 2.0+) ------------------------------------------------------------
Parameter:
AX = 4f0aH
BL = 00H Position und Länge des VBE_PM_API_Block ermitteln
--- VBE 2.0ß+ ----------------------------------------------------
01H Position und Länge für Code der Funktion 4f05H ermitteln 02H Position und Länge für Code der Funktion 4f07H ermitteln 03H Position und Länge für Code der Funktion 4f09H ermitteln
HINWEIS: Die Funktionen 01H bis 03H werden erst seit VBE 2.0 Beta, also (noch) viel zu selten, unterstützt, so daß man meist gezwungen ist, die gesamte 32bit PM-API zu kopieren. :(*)
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen 02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
CX = Länge der ab ES:DI beginnenden PM-API (bzw. Funktion) in Bytes
ES:DI = Zeiger (16:16, RM) auf VBE_PM_API_Block bzw. Funktionscode
{ Struktur: VBE_PM_API_Block
Ofs Typ Bezeichnung
000H W OfsFunc4f05H 002H W OfsFunc4f07H 004H W OfsFunc4f09H 006H W OfsPortsMem 008H ? : Variablen der VBE und Funktionscode ???H ?
}
Vorab einige grundsätzliche Dinge:
Die 16:16 Adresse, die von der Funktion 4f0a.00H zurückgegeben wird (ES:DI), zeigt zwar auf eine Tabelle, der man auch im Real Address Mode die direkten Einsprungpunkte (bzw. den Funktionscode) der Funktionen 4f05H, 4f07H sowie 4f09H entnehmen kann, bei den Funktionen handelt es sich aber um Code Frag- mente, die ausschließlich für 32bit Protected Mode CSs ausgelegt sind.
Der Versuch, eine der Funktionen im RM direkt anzuspringen, dürfte also mit großer Wahrscheinlichkeit kläglich scheitern... ;)
Um die Funktionen im 32bit PM direkt anspringen zu können, müssen zwei ent- sprechende Selektoren (Code und Data, Limit jeweils 64 KByte) gemapped, und ein 32bit Stack zur Verfügung gestellt werden.
Die Selektoren können sich im GDT oder einem LDT befinden, müssen aber immer zugänglich sein, sobald die VBE im PM aufgerufen wird.
Der 32bit Stack muß groß genug sein, um sowohl VBE-Routinen, als auch alle möglicherweise innerhalb der VBE auftretenden Interrupts zu bedienen, da die VBE grundsätzlich keine Stack Switches vornimmt, sondern immer den Stack des Aufrufers benutzt, solange Interrupts zugelassen sind (gilt auch für NMIs).
Schließlich sollte der Aufrufer noch sicherstellen, daß sein Privilege Level überhaupt ausreicht, um I/O Ports und Memory anzusprechen, die von der VBE bei der Durchführung einer Routine u.U. benutzt werden (siehe dazu auch die Erläuterungen zu VBE_PM_API_Block.OfsPortsMem weiter unten).
OfsFunc4f05H, Offset 00H, Word:
Dieses Feld enthält den Offset (relativ zu ES:DI) zum PM-Entrypoint der Funktion 4f05H (Display Window Control).
Parameter und Rückgabewerte entsprechen exakt der Standardfunktion 4f05H:
Parameter:
AX = 4f05H
BH = 00H Window-Startadresse setzen (WinGranularity-Units!) 01H Window-Startadresse ermitteln (WinGranularity-Units!)
BL = 00H Window A setzen/ermitteln = 01H Window B setzen/ermitteln
DX = Neue Window-Startadresse im Video-RAM (nur Funktion BH = 00H)
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen 02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
DX = Aktuelle Startadresse (nur Funktion BH = 01H)
OfsFunc4f07H, Offset 02H, Word:
Dieses Feld enthält den Offset (relativ zu ES:DI) zum PM-Entrypoint der Funktion 4f07H (Set/Get Display Start).
HINWEIS: Bei direktem Ansprung der Funktion 4f07H über die PM-API muss für das Setzen der Display Start Adress ein anderes Format benutzt werden, als beim Aufruf der Standardfunktion!
Parameter:
AX = 4f07H
BL = 00H Display Start setzen (unmittelbar) 01H Display Start ermitteln 80H Display Start setzen (während Vertical Retrace)
BH = 00H zwingend vorgegeben (reserviert)
--- nur Funktionen BL <> 01H -----------------------------------------
CX = Lo-Byte der neuen Display Start Address (!)
DX = Hi-Byte der neuen Display Start Address (!)
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen 02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
--- nur Funktion BL = 01H --------------------------------------------
CX = erstes dargestelltes Pixel (X-Pos der oberen linken Ecke)
DX = erste dargestellte Zeile (Y-Pos der oberen linken Ecke)
OfsFunc4f09H, Offset 04H, Word:
Dieses Feld enthält den Offset (relativ zu ES:DI) zum PM-Entrypoint der Funktion 4f09H (Set/Get Palette Data).
Parameter und Rückgabewerte entsprechen exakt der Standardfunktion 4f09H:
Parameter:
AX = 4f09H
BL = 00H Primary Palette setzen (unmittelbar) 01H Primary Palette ermitteln 02H Secondary Palette setzen 03H Secondary Palette ermitteln 80H Primary Palette setzen (während VRetrace)
CX = Anzahl zu setzende/ermittelnde Palettenregister f. Aktion [BL]
DX = Nummer des ersten Palettenregisters für Aktion [BL]
ES:DI = Zeiger auf zu benutzenden Palettenpuffer.
HINWEIS: Das Palettenformat entspricht NICHT dem RGB-Standard, d.h. die Farbe eines Registers wird nicht nur durch drei RGB-Bytes definiert, sondern muß zusätzlich ein folgendes Reserved-Byte enthalten.
M.a.W. also: Red, Green, Blue, Rsvd, Red, ...
Rückgabe:
AL = 4fH VESA BIOS Extension vorhanden # 4fH keine VBE vorhanden
AH = 00H ok 01H fehlgeschlagen 02H Funktion wird von Hardware nicht unterstützt 03H Funktion im aktuellen Videomodus nicht erlaubt
ES:DI = gefüllter Puffer (ermittelte Palette, nur BL = 01H oder 03H)
OfsPortsMem, Offset 06H, Word:
Dieses Feld enthält den Offset (relativ zu ES:DI) zu einer Liste von I/O Ports und Memory, für die der Aufrufer u.U. entsprechenden Privilege Level benötigt, um während eines direkten Funktionsaufrufes überhaupt auf sie zugreifen zu dürfen, oder Null, falls nicht erforderlich oder unterstützt.
Format:
[Port, Port,] ... EOL, [MemAddress, Length, MemAddress, Length,] ... EOL
Word Word Word DWord Word DWord Word Word
Beispiel I/O Ports only:
+-------------------------------------- Port: 03deH | +-------------------------------- Port: 03dfH | | +-------------------------- EOL (I/O Ports) | | | +-------------------- EOL (Memory) +-+-+ +-+-+ +-+-+ +-+-+
de 03 df 03 ff ff ff ff
Beispiel Memory only:
+-------------------------------------- EOL (I/O Ports) | +----------------------------- MemAddress: 000de800H | | +-------------------- Length: 200H Bytes | | | +-------------- EOL (Memory) +-+-+ +----+----+ +-+-+ +-+-+
ff ff 00 e8 0d 00 02 00 ff ff
Beispiel I/O Ports UND Memory:
+-------------------------------------- Port: 03deH | +-------------------------------- Port: 03dfH | | +-------------------------- EOL (I/O Ports) | | | +----------------- MemAddress: 000de800H | | | | +-------- Length: 200H Bytes | | | | | +-- EOL (Memory) +-+-+ +-+-+ +-+-+ +----+----+ +-+-+ +-+-+
de 03 df 03 ff ff 00 e8 0d 00 02 00 ff ff
HINWEIS: Auch falls es sich beim aktiven Videomode um ein LFBM handelt, so ist die Adresse des Linear Frame Buffers NIE in der Liste ent- halten, sondern grundsätzlich über VBEModeInfoBlock.PhysBasePtr (siehe Funktion 4f01H) in Erfahrung zu bringen.
LFBM (Linear Frame Buffer Model)
Juergen Thelen 2:2450/55.5:
Wer sich als Programmierer mit der Entwicklung von Spielen, Demos und Intros befasst, weiß sicher die Simplizität des Videomode 13H bzw. die Performance- Vorteile von Mode X Grafikmodis in bestimmten Bereichen (z.B. PolyFills) zu schätzen.
Doch wie sieht es in höher auflösenden Grafikmodi, wie z.B. 640x480x256 aus?
Nun ja, erstmal schlecht... ;)
Der offensichtlichste Grund hierfür ist, daß das zu bewegende Datenvolumen i.d.R. drastisch anwächst. Legt man beispielsweise 320x200x256 zugrunde, muß man bei 640x480x256 bereits nahezu die fünffache (!) Datenmenge ins RAM der Grafikkarte bewegen, um ein komplettes Bild zu füllen. Das ist nun mal der Preis für höher auflösende Grafikmodi... &)
Welchen Einbruch das bei der Performance auslöst, hängt natürlich davon ab, für welche Zwecke ein höher auflösender Grafikmodus eingesetzt wird. Solange es sich nicht um Animationen handelt, wird man bei den heute üblichen CPUs und Grafikkarten i.d.R. kaum einen sichtbaren Performanceverlust bemerken.
Anders sieht das ganze natürlich bei Animationen aus, speziell wenn es sich auch noch um Full Screen Anims (Animationen, bei denen für jedes Frame immer der komplette Bildschirm mit neuen Daten gefüllt werden muß) handelt.
Da heute im allgemeinen das System-RAM eines Rechners immer noch schneller ist, als Grafik-RAM, wird man zur Steigerung der Performance natürlich seine Szenarios im System-RAM vorrendern bzw. aufbauen. So weit, so gut... ;)
Nun müssen die Szenarios natürlich aber auch irgendwann einmal im Grafik-RAM landen, und genau hier taucht das zweite Hauptproblem bei höher auflösenden Grafikmodi auf: das Windowed Banking Model (WBM).
Das WBM bringt zwar den Vorteil, das es von nahezu allen VGA/SVGA-Karten und für alle möglichen Grafikmodi unterstützt wird, bringt aber leider auch den großen Nachteil mit sich, daß die Performance mit steigender Auflösung immer schlechter wird (zumindest, soweit es Full Screen Anims betrifft).
Die schlechte Performance entsteht durch das Konzept des WBMs an sich: das sogenannte Banking. Der Programmierer kann im WBM immer nur auf einen ein- zelnen, bestimmten Ausschnitt des Grafik-RAMs (eben die aktive Bank) zugrei- fen. Will er eine andere Bank adressieren, muß diese erst aktiviert werden.
Mal ganz davon abgesehen, daß dazu in der Regel ein INT notwendig ist, wenn man sich nicht umfangreiche Chip-Detection und Low Level Bankswitch-Routinen aufhalsen will, kommen noch zwei weitere Nachteile hinzu: erstens muß die alte Bank von der VGA/SVGA aus dem CPU-Adressraum (Video-RAM Segment a000H) aus-, und die neue Bank eingeblendet werden (was entsprechende Zeit frisst), und zweitens ist die maximale Bankgröße zwangsläufig auf 64 KByte begrenzt.
Das wiederum bedeutet, daß man sich diesen Performance-Killer nicht nur ein- mal, sondern gleich mehrfach einhandelt. Bei 640x480x256 beispielsweise sind das selbst im Idealfall (Banksize 64 KByte) fünf Bankswitches je Frame, von noch kleineren Bank-Größen (ältere Karten haben mitunter nur 4 KByte große Banks) ganz zu schweigen...
Es wäre also schön, wenn man das Grafik-RAM komplett in einem Stück, anstatt immer nur häppchenweise ansprechen könnte. Viele Hersteller von Grafikchips waren sich dessen natürlich bewußt, und implementierten bereits in neueren VGAs, spätestens aber in ihren SVGAs das Linear Frame Buffer Model (LFBM).
Im LFBM lässt sich das gesamte Grafik-RAM ohne Umweg direkt physikalisch an- sprechen. Alle Nachteile des WBM-Konzepts fallen damit weg. Man sollte also eigentlich meinen, die Zeichen für Top-Performance stehen gut...
Stehen sie auch ;), aber leider gilt das nicht uneingeschränkt für alle CPU- Betriebsmodi. Speziell im "herkömmlichen" RM mit seinem segmentierten 16:16 Speichermodell bringt das LFBM nur wenig (bei Auflösungen bis 64 KByte) bzw. kaum noch erwähnenswerte Vorteile (Auflösungen größer 64 KByte).
Problem Nummer eins ist, daß fast der gesamte im RM adressierbare Adressraum (bis ffff:ffefH) bereits fest vergeben bzw. belegt ist. Folglich bleibt den Herstellern meist nichts anderes übrig, als den Linear Frame Buffer physika- lisch oberhalb des Real Memorys anzusiedeln. Das wiederum führt aber zu Pro- blem Nummer zwei, denn Memory oberhalb der 1 MByte Grenze entzieht sich auf- grund der Natur des segmentierten Speichermodells bekanntlich direktem 16:16 Zugriff.
Also is' nix mit LFBMs im herkömmlichen RM?
Doch. Man könnte den Linear Frame Buffer über Speichermanager (XMS oder EMS) ansprechen. Was das allerdings für die Performance bedeutet, dürfte wohl je- dem einleuchten (speziell, wenn die Auflösung die magische 64 KByte Grenze überschreitet, denn dann kann man auch noch anfangen, selber seine RAM-Bänke zu verwalten)... &)
In einem Pseudo-Betriebsmodus wie RM/Flat4G (siehe dazu auch Topic BAW0015) sieht das ganze dagegen schon erheblich besser aus, denn hier entfallen ja schließlich alle Beschränkungen des RM 16:16 Speichermodells.
Ähnlich gut, wenn nicht sogar am besten, sieht es im Protected Mode aus. Im PM muß man zwar noch etwas mehr Aufwand betreiben, bevor ein Grafikmodus mit LFBM genutzt werden kann, aber auch der ist im Prinzip harmlos... :)
Ok. Was also brauchen wir alles, um das LFBM einsetzen zu können?
- Nun, als erstes müssen wir natürlich erst einmal irgendwie feststellen, ob von der angesprochenen VGA/SVGA das Linear Frame Buffering denn überhaupt unterstützt wird. Dazu haben wir drei Ansatzmöglichkeiten:
* VBE-Methode * I/O-Methode * PCI-Methode
VBE-Methode -----------
Mit VBE 2.0 (VESA BIOS Extension, siehe Topic BAW0021) wurde u.a. auch ein Feld namens ModeInfoBlock.PhysBasePtr definiert. Nach dem Aufruf der VBE- Funktion 4f01H (Return VBE Mode Information) ist das Feld mit der physika- lischen Adresse des Linear Frame Buffers gefüllt, vorausgesetzt natürlich, das LFBM wird für den angegebenen Videomodus überhaupt unterstützt... &)
Diese Methode ist imo aus mehreren Gründen die empfehlenswerteste. Sie ist simpel, kompatibel und bedarf nur wenig Zeit- und Kostenaufwand. Aber, wie sollte es auch anders sein ;), auch diese Methode hat ihren Haken...
Der Haken an der Sache ist ganz einfach der, daß es immer noch tonnenweise SVGAs gibt, die das LFBM schon seit VBE 1.x Zeiten beherrschen, aber mang- els entsprechender VBE-Funktion keine allgemeingültige Möglichkeit bieten, die Adresse des Linear Frame Buffers zu ermitteln, bzw. das LFBM für einen Videomodus zu aktivieren.
I/O-Methode -----------
Diese Methode ist die einzige, mit der sich die physikalische Adresse des Linear Frame Buffers von jeder LFBM-fähigen SVGA ermitteln lässt - voraus- gesetzt, man weiß, welche I/O-Ports bzw. Register für die Identifikation des Chipsets bzw. für die LFB-Adressermittlung denn zuständig sind... &)
Wer keine Lust, Zeit oder das Geld hat, um sich alle erforderlichen Doku- mentationen zu besorgen (oder das Rad ständig neu zu erfinden), kann sich umsehen, ob er irgendwo entsprechende Chipset-Libraries findet. Speziell in den 3D-Engines, die heutzutage bereits häufig als Freeware und inklusiv Sourcecodes angeboten werden, wird man oft fündig.
Wer will, kann sich auch einmal nach Finn Thoegersens VGADOC4B umsehen. In diesem Paket sind sehr viele Registersätze verschiedenster Chipsets doku- mentiert, sowie diverse Chipset Detection-Sources (Pascal) enthalten. Was allerdings LFB-relevante Dinge angeht, so muß man sich schon selbst durch die Registersets wühlen, da Finn die LFB-Thematik (I/O Ports und Register betreffend) nicht speziell behandelt oder ausgearbeitet hat...
Eine weitere Hilfe für den Programmierer könnte evtl. SDD (vormals UniVBE) sein. SDD verfügt über eine enorm große Bandbreite, was die Identifikation von Chipsets betrifft (etwa 200 Chipsets werden erkannt) und kann, soweit ich mich recht entsinne, in kleinen und nichtkommerziellen Projekten auch lizenzfrei mitvertrieben werden. Zudem kann man via SDD häufig gleich zwei Fliegen mit einer Klappe schlagen, da SDD so manche ältere SVGA software- seitig von VBE 1.x auf VBE 2.0 upgraden kann.
Der Hersteller von SDD, die Firma SciTech, kann natürlich auch nicht zau- bern, und hat nur das gemacht, was Programmierer in solchen Fällen halt so machen: I/O Ports und Register aus den Kartendokumentationen herausfischen und dann Dutzende kartenspezifische Treiber daraus bauen... ;)
Da SciTech meines Wissens bei größeren und kommerziellen Projekten Lizenz- gebühren für den Einsatz von SDD verlangt, dürfte bei so manchem Program- mierer zwangsläufig der Gedanke des Selberbauens aufkommen bzw. schon auf- gekommen sein... $>
Hierzu sei von meiner Seite nur angemerkt, daß man als Otto Normalprogger i.d.R. für jede originale Kartendokumentation kräftig blechen muss. Preise um die 30 bis 50 USD je Doku sind dabei durchaus normal (Erfahrungswerte). Warum die Hersteller von Grafikchipsets das so halten, und die Akzeptanz und Verbreitung ihrer Produkte nicht durch kostenlose Bereitstellung von Dokus beispielsweise als .PDF Files auf irgendeinem Internet-Server unter- stützen (wie es im CPU-Bereich z.B. lange üblich ist), ist mir ein Rätsel.
Togal. Wer bereit ist, überhaupt die Patte auszupacken, sollte imo vor dem wilden Aufkauf aller Dokus ;) in Betracht ziehen, ob es nicht vielleicht lohnenswerter ist, VESA-Mitglied zu werden. Der Mindestmitgliedschaftsbei- trag betrug meines Wissens einmal ca. DM 2000.- p.a. (umsatzabhängig). Da fast jeder Hersteller von Grafikchipsets ebenfalls VESA-Mitglied (SciTech übrigens auch) ist, evtl. eine lohnende Sache. Informiert Euch aber bitte vorher über den aktuellen Stand, da die hier gemachten Angaben 3 Jahre und älter sind... &)
PCI-Methode -----------
An diesen Strohhalm kann man sich klammern, wenn sowohl die VBE-, als auch die I/O-Methode zu keinem Ergebnis geführt hat. Da die PCI-Methode auf Low Level Programmierung des PCI-Busses beruht, scheiden damit natürlich auch zwangsläufig alle reinen ISA- und VLB-Systeme, sowie PCI-Systeme ohne ent- sprechend aktive PCI-Grafikkarte bereits im Vorfeld aus.
Wer mit der geschrumpften Zielgruppe leben kann (soweit man bei der immer weiter zunehmenden Verbreitung von PCI-Grafikkarten bzw. dem allmählichen Aussterben von reinen ISA-/VLB-Grafikkarten überhaupt noch von schrumpfen sprechen kann), braucht i.d.R. nichts weiter zu tun, als ein bestimmtes DWord ab Offset 10H aus dem Configuration Address Space (CAS) der Grafik- karte auszulesen, um zu erfahren, ob ein Linear Frame Buffer vorhanden ist und wenn ja, ob er direkt via Memory adressiert werden kann.
Der CAS ist Bestandteil jedes PCI Devices und ist über einen gewissen Be- reich in Format und Funktion genormt. Detaillierte Informationen über das CAS-Format bzw. den Zugriff auf den CAS über den PCI-Bus befinden sich im Topic BAW0023.
Um nach Grafikkarten auf dem PCI-Bus zu scannen, klappert man einfach alle Devices (bzw. dessen Functions) ab, liest aus dem jeweiligen CAS immer das Word von Offset 0aH aus, und prüft ob es sich dabei um einen der Werte von 0000H (alte VGAs), 0300H (VGAs) oder 0301H (SVGAs) handelt.
Doch zurück zum besagten DWord. Der Name des DWords an Offset 10H des CAS lautet gemäß PCI-Spezifikation Base0. Interessant ist für uns zunächst nur Bit 0 von Base0, denn dieses ReadOnly-Bit verrät uns, ob dieses PCI Device über einen I/O Port (Bit 0 = 1), oder direkt via Memory (Bit 0 = 0) adres- siert werden kann/muß.
Ist die Karte via Memory adressierbar, sollte man sich sicherheitshalber, d.h. aus Gründen der Kompatibilität, als nächstes einmal die Bits 1 und 2 von Base0 ansehen. Diese Bits sind zwar i.d.R. Null, was bedeutet, daß die Adresse des Linear Frame Buffers komplett in den Bits 4..31 enthalten ist, aber zukünftig kann es durchaus sein, daß dort 10b (2..1) steht, was be- deuten würde, daß auch noch das nächste DWord an Offset 14H zur Adressbil- dung miteinbezogen werden müsste (LFBAddress-Bits 32..63).
Allerdings ist die Adresse des LFB aber auch schon alles, was man sich zum Thema LFBM über den PCI-Bus holen kann. Wie man einen bestimmten Grafikmo- dus aktiviert, und ob zur Aktivierung des LFBM eventuell weitere Schritte erforderlich sind, lässt sich direkt über den PCI-Bus nämlich leider nicht in Erfahrung zu bringen.
Im Prinzip stehen wir hier vor einem ähnlichen Dilemma, wie wir es bereits von der I/O-Methode kennen. Ein nicht zu unterschätzender Vorteil der PCI- Methode ist jedoch, daß sich der Chipset leichter identifizieren lässt, da mehrere standardisierte Felder des CAS (z.B. VID, DID, RID, CLASSC) gute Unterscheidungskriterien an immer demselben Ort bieten und nicht, wie bei der I/O-Methode üblich, alle möglichen I/O-Ports abgegrast werden müssen.
- Steht fest, daß für einen gewählten Videomodus das Linear Frame Buffering unterstützt wird, können wir an dessen Aktivierung gehen. Beschrieben wer- den kann hier natürlich nur, wie dies bei einer VBE 2.0 kompatiblen SVGA funktioniert, da anderenfalls ja je nach Chipset die unterschiedlichsten I/O Ports und/oder Register zu programmieren sind... &)
Soweit es VBE 2.0 kompatible SVGAs betrifft, hat man es ganz einfach. Die Funktion 4f02H (Set VBE Mode) lässt nämlich nicht nur die Auswahl des Vi- deomodus zu, sondern der Programmierer kann zusätzlich über ein gesetztes Bit 14 im BX-Register auch anweisen, daß der Videomodus im LFBM betrieben werden soll, sprich BX = 4???H = LFBM. Schön einfach, gell? ;)
Nicht vergessen: das funktioniert nur, wenn die Karte im gewählten Video- modus das LFBM überhaupt unterstützt. Ein Videomodus kann unter VBE grund- sätzlich nur dann im LFBM betrieben werden, wenn man ein gesetztes Bit 7 in ModeInfoBlock.ModeAttributes (s. Funktion 4f01H, Topic BAW022) findet, bzw. ModeInfoBlock.PhysBasePtr ungleich Null ist.
- So. Was den Real Address Mode ("herkömmlich" und RM/Flat4G) angeht, war's das schon. Der Videomodus ist nun im LFBM aktiv und kann nach Herzenslust direkt linear (32bit, RM/Flat4G) bzw. über XMS/EMS adressiert werden.
Anders im Protected Mode, hier sind vor dem ersten Zugriff noch einige zu- sätzliche Schritte notwendig, um die Schutzmechanismen des Multitaskings nicht zu unterlaufen. Um das zu vermeiden, benötigen wir einen Selector, der uns den Zugriff auf den Linear Frame Buffer gestattet.
Da zur Protected Mode Programmierung meist irgendeine Hochsprache benutzt wird, von denen wiederum so gut wie alle auch das DPMI (DOS Protected Mode Interface) unterstützen, haben wir schonmal einen universellen Ansatz.
Um über DPMI einen eigenen Selector zu bekommen, ist INT 31.0000H gedacht. Das Problem hierbei ist, daß wir über diese Funktion zwar einen Selector auf einen DATA Segment Descriptor im LDT (Local Descriptor Table) unseres Tasks erhalten, aber Base und Limit vom DPMI automatisch auf Null gesetzt werden. Das Limitproblem ist schnell vom Tisch, da sich das Limit über den INT 31.0008H leicht verändern lässt. Was allerdings Base angeht, wird es etwas schwieriger. Zwar gibt es auch hierfür eine entsprechenden Funktion (INT 31.0007H), aber diese DPMI-Funktion erwartet neben dem Selector zu- sätzlich noch die gewünschte lineare Basisadresse des Segmentes. Wir haben aber leider bis jetzt nur die physikalische (aus ModeInfoBlock.PhysBasePtr nach VBE-Funktion 4f01H). Glücklicherweise hat aber das DPMI auch hierfür eine Lösung parat: man braucht nur die Funktion INT 31.0800H mit der phy- sikalischen Adresse zu füttern, und schon mapped einem das DPMI den Wert in eine lineare Adresse um... ;)
Setzt man Base dann über INT 31.0007H auf diese lineare Adresse, hat man einen wunderschönen Selector auf den Linear Frame Buffer und einem Zugriff steht nichts mehr im Weg. Ist man auf Performance aus, empfiehlt sich imo noch, ein bestimmtes Segmentregister (ich benutze beispielsweise immer GS) nur für LFB-Zugriffe zu reservieren, da Segmentregister-Loads im PM einen Haufen Rechenzeit verbraten... ;)
Abhängig von der Hochsprache, Art des PM (32bit/16bit) und Privilege Level (CPL) lohnen sich teilweise eventuell auch noch entsprechende Änderungen der Descriptor Access Rights über die DPMI-Funktion 31.0009H, aber das nur am Rande... &)
Hmmm, mehr fällt mir zum LFBM nicht ein. Außer vielleicht, daß es ganz sinn- voll sein könnte, wenn alle Leser der ASMFAQ sammeln würden, was sie selbst an technischen Daten zur I/O- und PCI-Methode beitragen können.
Wenn also jemand für eine oder mehrere ganz bestimmte Non-VBE 2.0 VGAs/SVGAs weiß, wie deren Chipsets identifiziert und/oder das LFBM aktiviert werden, dann immer her mit den Daten. Ich würde mich bereit erklären, die Daten zu sammeln und der ASMFAQ als Zusatzdatei beizulegen.
In diesem Sinne... ;)
CAS (Configuration Address Space, PCI)
Juergen Thelen 2:2450/55.5:
Jedes PCI-Device, also z.B. eine PCI-Grafikkarte, besitzt einen sogenannten Configuration Address Space (CAS). Dieser CAS ist über eine gewisse Strecke in Format und Funktion genormt und enthält zahlreiche für den Programmierer hilfreiche Informationen, sei es nun zu reinen Identifikationszwecken oder die Programmierung bzw. technische Charakteristik des Devices betreffend.
Um auf diesen CAS zuzugreifen hat man zwei prinzipielle Möglichkeiten: ent- weder über entsprechende Funktionen der PCI API (INT 1a.b1??H), oder direkt Low Level über bestimmte I/O-Ports.
Low Level Freaks werden wohl aus den üblichen Gründen (Performance, Control) so oder so die PCI API nicht nutzen wollen, aber auch reine Hochsprachenpro- grammierer kommen bei CAS-Zugriffen u.U. nicht an der hardwarenahen Progra- mierung vorbei. Der Grund ist simpel: die PCI API existiert leider erst seit PCI BIOS Revision 2.0c... &)
Wem ältere PCI BIOSs schnuppe sind, dem reichen natürlich INT 1a.b108H bis INT 1a.b10aH (16bit) bzw. 1a.b188H bis 1a.b18aH (32bit) zum Auslesen (Byte, Word, DWord) bzw. die entsprechenden sechs Funktionen zum Beschreiben des CAS (INT 1a.b10bH bis 1a.b10dH (16bit) bzw. 1a.b18bH bis 1a.b18dH (32bit)).
Wer kein PCI Device durchflutschen ;) lassen will, muß tiefer in die Materie eindringen. Um Low Level auf den CAS von PCI Devices zuzugreifen, existieren zwei Mechanismen, genannt CASAM1 und CASAM2 (CASAM = Configuration Address Space Access Mechanism). Kurioserweise gibt es ersteren Mechanismus (CASAM1) erst seit August 1994 (mit Einführung der PCI-BIOS Revision 2.10). Davor gab es immer nur CASAM2... &)
Togal. Bevor wir mit einem der beiden CASAMs auf einen CAS zugreifen können, müssen wir natürlich erst einmal wissen, welcher Mechanismus für den PCI-Bus unterstützt wird. Auf PCI-Bus #0 abgestimmte Beispielsources für die Erken- nung des jeweiligen CASAMs befinden sich in den Topics LSG0019 (CASAM1) bzw. LSG0020 (für CASAM2).
Steht einmal fest, welcher CASAM zu verwenden ist, muß der Zugriff natürlich auch entsprechend den Richtlinien des jeweiligen Mechanismus erfolgen. Diese werden hier im folgenden beschrieben:
Configuration Address Space Access Mechanism 1 (CASAM1): --------------------------------------------------------
Mit CASAM1 können theoretisch bis zu 256 PCI-Busse mit jeweils bis zu 32 De- vices, welche ihrerseits wiederum jeweils bis zu 8 Functions (Units) haben können, angesprochen werden.
In der Praxis wird man allerdings überwiegend auf Systeme treffen, die nur über einen einzelnen Bus (PCI-Bus #0), oder vielleicht maximal 5 bis 10 PCI- Busse verfügen (z.B. bei SCSI). Die 32 theoretisch möglichen Devices pro Bus werden übrigens i.d.R. auch nie erreicht, hier sind eher 3 bis 6 üblich. Ur- sache hierfür ist, daß ein PCI-Bus meist nur 10 bis 15 sogenannte Loads ver- trägt, wobei jede PCI-Steckkarte in einem PCI-Slot zwei, und jeder on-board befindliche PCI-Controller einen dieser Loads verbrät...
Obwohl es sich bei CASAM1 im Prinzip um einen ganz simplen Mechanismus han- delt, so ist er doch mit einer Besonderheit behaftet, die speziell Einstei- ger schnell zum Stolpern bringen kann. Diese Besonderheit besteht darin, daß unter CASAM1 immer nur eine bestimmte 4-Byte-Portion des gesamten CAS sicht- bar ist und bearbeitet werden kann. Hinzu kommt, das dieses vier Byte breite Fenster nicht beliebig "über" dem CAS, sondern ausschließlich an 0MOD4-Boun- daries (0MOD4 = Adresse / 4 = x, Rest 0) positioniert werden kann und darf.
Mit anderen Worten, um beispielsweise das Byte von Offset 7 des CAS auslesen zu können, kann man nicht einfach das Fenster auf CAS-Offset 7 setzen, son- dern muß es an CAS-Offset 4 positionieren und dann das letzte Byte des vier Byte breiten Fensters auslesen. Etwas umständlich, aber so isses nun mal. ;)
Togal. Um via CASAM1 auf den CAS eines Devices zuzugreifen, muß zuerst das besagte 4 Byte breite Fenster entsprechend positioniert werden. Dazu ist ein codiertes DWord an Port cf8H (auch CONFADD genannt) zu schreiben, das neben Bus, Device, Function und gewünschtem CAS-Offset (0MOD4-Boundary) unbedingt auch ein gesetztes Bit 31 (CASE) enthalten muß, um überhaupt die Zugriffs- erlaubnis auf das CAS-Fenster zu erhalten. Danach kann das Fenster dann über die Ports cfcH bis cffH (auch CONFDATA genannt) beliebig (Byte, Word, DWord) ausgelesen bzw. beschrieben werden (soweit möglich):
CASAM1: CONFADD, Port 0cf8H: ---------------------------- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|1 1 1 1 1 1 1|1 1 1 1 1 1 1 1| | | | | |f|e d c b a 9 8|7 6 5 4 3 2 1 0|f e d c b|a 9 8|7 6 5 4 3 2|1 0| | | | | | | | | |C| | | | | | | |A| | | | | | | |S| | | | | | | |E|0 0 0 0 0 0 0| BusNo | DevNo | Fnc | CfgOfs |0 0| +-+-------------+---------------+---------+-----+-----------+---+
CASE = Configuration Address Space Enable (1) BusNo = Anzusprechender PCI-Bus (0..255) DevNo = Anzusprechendes Device (0..31) innerhalb des Busses Fnc = Anzusprechende Function (Unit, 0..7) innerhalb des Devices CfgOfs = 0MOD4-Index (!) des gewünschten CAS-Offset (0..63)
00H CAS:00..03H 01H CAS:04..07H : 3fH CAS:fc..ffH
CASAM1: CONFDATA, Port 0cfcH..cffH: -----------------------------------
+-------------------------- Port cfcH --------------------------+ DWord +---------- Port cfeH ----------+---------- Port cfcH ----------+ Word +-- Port cffH --+-- Port cfeH --+-- Port cfdH --+-- Port cfcH --+ Byte +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0| +---------------------------------------------------------------+ +- CfgOfs*4[3] -+- CfgOfs*4[2] -+- CfgOfs*4[1] -+- CfgOfs*4[0] -+
Configuration Address Space Access Mechanism 2 (CASAM2): --------------------------------------------------------
Synonym zu CASAM1 können auch mit CASAM2 theoretisch bis zu 256 PCI-Busse angesprochen werden. Anders als bei CASAM1 sind bei CASAM2 jedoch lediglich bis zu 16 Devices je Bus möglich, die aber nach wie vor jeweils ihre bis zu zu 8 Functions (Units) haben können.
Die Handhabung von CASAM2 ist leider nicht ganz so simpel und bequem, wie es bei CASAM1 der Fall ist. Für den Zugriff via CASAM2 müssen drei verschiedene Ports programmiert werden, von denen für einen sogar erst dessen Adresse be- rechnet werden muß. Das hört sich zunächst zwar kompliziert an, aber wie wir gleich feststellen werden, ist es das bei näherer Betrachtung gar nicht.. &)
Der prinzipielle Unterschied zu CASAM1 besteht darin, daß sowohl DevNo als auch CfgOfs bei CASAM2 nicht mehr in einen Port geschrieben werden, sondern über sie die Adresse des Ports berechnet werden muss, über den der CAS aus- gelesen bzw. beschrieben werden kann. Doch der Reihe nach: zuerst schreibt man mal die gewünschte BusNo nach Port cfaH (auch FWDREG genannt). Danach kann man über Port cf8H die gewünschte Fnc (Bits 1..3) auswählen und muß zu- sätzlich die Bits 4..7 (Key) auf irgendwas ungleich Null setzen, um auf den CAS zugreifen zu können. Vorsicht, Stolperfalle: unter CASAM1 wird Port cf8H CONFADD genannt und ist 32bit breit. Bei CASAM2 dagegen nennt sich der Port normalerweise CSE (Configuration Space Enable) und ist nur 8bit breit.
Wie dem auch sei, kommen wir jetzt zu der bereits angesprochenen Ermittlung der Portadresse für den Zugriff auf den CAS. Unter CASAM2 wird der CAS nicht wie bei CASAM1 über Port cfcH, sondern einen der Ports im Bereich c000H bis cfffH ausgelesen bzw. beschrieben. Der variable Teil sind dabei die unteren 12 Bits, von denen über die Bits 11..8 das Device (die DevNo), und über die Bits 7..0 schließlich der CfgOfs definiert wird.
Häh? Bits 7..0 für den CfgOfs? Etwa kein 4 Byte CAS-Fenster wie bei CASAM1?
Korrekt. Unter CASAM2 kann jeder beliebige Offset von 0 bis 255 im CAS ange- sprochen werden. Zu meiner Schande muß ich allerdings gestehen, daß ich über keinerlei Unterlagen verfüge, aus denen hervorgeht, ob diese Ports auch für Word- und DWord-Zugriffe (und nicht nur für Bytezugriffe) geeignet sind und falls ja, ob es Boundary-Regeln für sie gibt. Sollte jemand noch im Besitz irgendeiner älteren PCI-Spezifikation (vor 2.1) oder ähnlich sicheren Quelle sein, bitte ich zur Klarifizierung um entsprechende Mitteilungen. Danke.. ;)
Ok. Hier die Ports zu CASAM2 nochmal in komprimierter Form:
CASAM2: FWDREG, Port cfaH: -------------------------- +-+-+-+-+-+-+-+-+ |7 6 5 4 3 2 1 0| | | | BusNo | +---------------+
BusNo = Anzusprechender PCI-Bus (0..255)
HINWEIS: Es soll Systeme geben (z.B. PC98, Japan), bei denen das FWDREG nicht auf Port cfaH, sondern auf cf9H gemapped ist. Hatte selbst noch nie ein solches System in den Fingern, kann das also weder bestätigen, noch dementieren... ;)
CASAM2: CSE, Port cf8H: ----------------------- +-+-+-+-+-+-+-+-+ |7 6 5 4|3 2 1|0| | | | | | | |S| | | |C| | Key | Fnc |E| +-------+-----+-+
Key = Key (auf <> 0 setzen, wirkt ähnlich wie CASE bei CASAM1) Fnc = Anzusprechende Function (Unit 0..7) innerhalb des Devices SCE = Special Cycle Enable (nicht enablen, sondern = 0 setzen!)
CASAM2: Adresse des Zugriffsports: ---------------------------------- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |f|e|d|c|b a 9 8|7 6 5 4 3 2 1 0| | | | | | | | |1|1|0|0| DevNo | CfgOfs | +-+-+-+-+-------+---------------+
DevNo = Anzusprechendes Device (0..15) innerhalb des Busses CfgOfs = Index des Bytes (!) im CAS (0..255)
Aufbau des Configuration Address Space --------------------------------------
Die ersten 16 Bytes des CAS sind in Format und Inhalt für jedes PCI Device gleich und eignen sich daher hervorragend für universelle Identifikationen und ähnliche Unterfangen.
Das CAS-Format ab dem siebzehnten Byte (dem sogenannten Header) ist dagegen bereits unterschiedlich. Bis jetzt existieren drei verschiedene Headertypen: Non-Bridge (00H), PCI-to-PCI Bridge (01H) und PCI-to-CardBus-Bridge (02H).
Interessant für die allgemeine Programmierung ist imo nur der Headertyp 00H (Non-Bridge), daher beschränke ich mich bei der Dokumentation auch lediglich auf eben diesen (bis zum 64sten Byte, d.h. ab dem devicespezifischen Bereich ist Schluß).
Zu Fragen das Format der zwei anderen Bridge-Headertypen betreffend, sei auf andere Dokus (z.B. Ralf Browns Interrupt List, siehe QLL0001) verwiesen. Ich wollte das Topic nämlich doch gerne noch fertig bekommen, bevor ich ein bib- lisches Alter erreiche... ;)
Was meiner Meinung nach allerdings nicht fehlen durfte, war zum einen eine Liste mit allen VendorIDs, und zum anderen eine Liste, die es möglich machen soll, jedes PCI-Device über VID (VendorID), DID (DeviceID), RID (RevisionID) sowie CLASSC (BaseClass, Sub-Class, Programming Interface) und TYP (Single/ Multi Function Device) zu identifizieren.
Zu diesem Zweck sind ab Release 01/98 zwei entsprechende Dateien (namentlich PCI_VID.LST und PCI_DEV.LST) im ASM86FAQ-Paket enthalten. Zur Unterstützung derjeniger, die evtl. zur Erweiterung der PCI_DEV.LST beitragen können und möchten, wurde der FAQ zudem ein Testprogramm namens PCI_DEV.EXE beigefügt. Das Programm scannt alle PCI-Busse nach Single und Multifunction Devices ab und gibt den Inhalt der relevanten Felder aus.
Gegen Beiträge zur PCI_VID.LST habe ich natürlich auch nichts einzuwenden ;)
Vorab nun erst einmal alle Felder eines Non-Bridge CAS als Übersicht:
+--------+-----+----------+----------------------------------------------+ | Offset | Typ | Feldname | Funktion | +--------+-----+----------+----------------------------------------------+ | 00 | W | VID | VendorID | +--------+-----+----------+----------------------------------------------+ | 02 | W | DID | DeviceID | +--------+-----+----------+----------------------------------------------+ | 04 | W | PCICMD | PCI Command Register | +--------+-----+----------+----------------------------------------------+ | 06 | W | PCISTS | PCI Status Register | +--------+-----+----------+----------------------------------------------+ | 08 | B | RID | RevisionID | +--------+-----+----------+----------------------------------------------+ | 09 | B | PIF | Programming Interface -+ | +--------+-----+----------+ | + | 0a | B | SCC | Sub-Class Code --------+- CLASSC | +--------+-----+----------+ | + | 0b | B | BCC | BaseClass Code --------+ | +--------+-----+----------+----------------------------------------------+ | 0c | B | CLS | Cache Line Size | +--------+-----+----------+----------------------------------------------+ | 0d | B | MLT | Master Latency Timer | +--------+-----+----------+----------------------------------------------+ | 0e | B | HEADT | Header Type | +--------+-----+----------+----------------------------------------------+ | 0f | B | BIST | Built-In Self-Test | +--------+-----+----------+----------------------------------------------+ |±±± Non-Bridge Header ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±| +--------+-----+----------+----------------------------------------------+ | 10 | D | BASE0 | Base Address 0 | +--------+-----+----------+----------------------------------------------+ | 14 | D | BASE1 | Base Address 1 | +--------+-----+----------+----------------------------------------------+ | 18 | D | BASE2 | Base Address 2 | +--------+-----+----------+----------------------------------------------+ | 1c | D | BASE3 | Base Address 3 | +--------+-----+----------+----------------------------------------------+ | 20 | D | BASE4 | Base Address 4 | +--------+-----+----------+----------------------------------------------+ | 24 | D | BASE5 | Base Address 5 | +--------+-----+----------+----------------------------------------------+ | 28 | D | CBCISP | CardBus CIS Pointer 2.10+ | +--------+-----+----------+----------------------------------------------+ | 2c | W | SSVID | Subsystem VendorID 2.10+ | +--------+-----+----------+----------------------------------------------+ | 2e | W | SSID | SubsystemID 2.10+ | +--------+-----+----------+----------------------------------------------+ | 30 | D | XROMBASE | Expansion ROM Address | +--------+-----+----------+----------------------------------------------+ | 34 | D | OFSNCL | Offset to New Capabilities List | +--------+-----+----------+----------------------------------------------+ | 38 | 4 B | - | reserviert | +--------+-----+----------+----------------------------------------------+ | 3c | B | IRQL | IRQ Line | +--------+-----+----------+----------------------------------------------+ | 3d | B | IRQP | IRQ Pin | +--------+-----+----------+----------------------------------------------+ | 3e | B | MINGRANT | Minimum Granted | +--------+-----+----------+----------------------------------------------+ | 3f | B | MAXLAT | Maximum Latency | +--------+-----+----------+----------------------------------------------+
Ok. Bevor wir nun zur Funktion der CAS-Felder im Einzelnen kommen, noch fol- gende Anmerkung: soweit generell gültig, werden präzise Angaben über den für das jeweilige Feld erlaubten Zugriffstyp bzw. Bit-Typ gemacht:
[R-] ReadOnly [RW] Read/Write [R/WC] Read/WriteClear (Bit = 1 -> Bit = 0. Bit = 0 hat keinen Effekt).
Bei stark devicespezifisch orientierten Feldern (bzw. deren Bits), wie z.B. CAS.PCICMD und CAS.PCISTS, sind genaue Angaben über den oder die möglichen Zugriffstypen bzw. Bit-Typ logischerweise nicht möglich, daher wird folgende Kennzeichnung benutzt:
[R*] Feld bzw. Bit lesbar. Rückgabewert beim Lesen bzw. Verhalten bei Schreibzugriffen aber rein devicespezifisch. Bits können z.B. auf 0 oder 1 gehardwired ([R-]), genausogut aber auch variabel ([RW]) programmierbar sein, oder speziell reagieren ([R/WC]).
CAS.VID, Vendor ID, Offset 00H, Word, [R-]: +++++++++++++++++++++++++++++++++++++++++++
Hersteller-Identifikationsnummer. Eine umfangreiche Liste mit über 750 VIDs ist als Datei PCI_VID.LST im ASM86FAQ-Paket enthalten... ;)
VIDs werden von der PCI SIG (PCI Special Interest Group, www.pcisig.com) zu- geteilt und verwaltet. Leider ist dieser Verein Programmierern gegenüber imo weder freundlich eingestellt (eigene Erfahrungswerte, diverse E-Mails wurden einfach ignoriert, bzw. nie beantwortet. PCI Spezifikation nur gegen Entgelt erhältlich (ca. 40 USD nach Deutschland)), noch ist deren öffentlich zugäng- liche VID-Liste (Webserver) sonderlich aktuell (letzter Update war 05/97)...
Vielleicht muß man dort erst Mitglied werden (2500 USD p.a.), um korrekt be- handelt zu werden. Keine Ahnung, urteilt selbst... &)
CAS.DID, Device ID, Offset 02H, Word, [R-]: +++++++++++++++++++++++++++++++++++++++++++
Geräte-Identifikationsnummer des Herstellers (s. auch Datei PCI_DEV.LST).
CAS.PCICMD, PCI Command, Offset 04H, Word, [R*]: ++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |f e d c b a|9|8|7|6|5|4|3|2|1|0| | | | | | | | | | | | | | |F|S| |P| | | | | | | | |B|E| |E|V|M| | | |I| | |2|R|W|R|G|W|S|B|M|O| | |B|R|S|R|P|I|C|M|S|S| | reserv. |E|E|C|E|S|E|E|E|E|E| +-----------+-+-+-+-+-+-+-+-+-+-+
HINWEIS: Die erlaubten Zugriffsarten für Bits dieses Feldes sind devicespe- zifisch. Größtenteils handelt es sich hier um [RW]-Bits, aber je nach Device können Bits auch auf 0 oder 1 gehardwired [R-] sein.
IOSE = I/O Space Enable. Zugriffe auf den I/O Adressraum des Devices zu- lassen (1) oder verbieten (0). Siehe auch CAS.H0.BASEn (10H..).
MSE = Memory Space Enable. Zugriffe auf den Memory Adressraum des Device erlauben (1) oder untersagen (0). Siehe auch CAS.H0.BASEn (10H..).
BME = Bus Master Enable. Device als Bus Master (1) oder Target (0) akti- vieren. Als Target darf ein Device keine PCI-Zugriffe generieren, um auf System Memory zuzugreifen, d.h. es unterliegt der Kontrolle eines anderen Devices oder Masters (z.B. EISA/ISA, oder DMA o.ä.).
SCE = Special Cycle Enable. Device soll auf via PCI ausgelöste Special Cycles reagieren (1), oder nicht (0). Existenz und Funktion dieser Special Cycles ist rein devicespezifisch, viele Devices unterstüt- zen aber zumindest die Typen 0 (Shutdown) und 1 (Halt).
MWIE = Memory Write and Invalidate Enable. Device darf als Bus Master das Kommando 0fH (also MWIE) generieren (1), oder nicht (0, = nur MW).
VGPS = VGA Palette Snoop (nur Grafikkarten): VGA-Standardverhalten bei Palettenzugriffen via DAC (0), oder Spezialverhalten (1), bei dem DAC-Writes auf eine besondere Weise verarbeitet werden.
PERRE = PERR# (Parity Error) Driver Enable. Device soll PERR#-Signal bei Auftritt eines Paritätsfehlers erzeugen (1), oder eben nicht (0).
WSC = Wait State Control. Address (Master) bzw. Data (Target) Stepping des Devices ein (1) oder aus (0).
SERRE = SERR# (System Error) Driver Enable. Device soll SERR#-Signal bei Auftritt eines Systemfehlers (ECC, usw.) generieren (1) oder nicht (0).
FB2BE = Fast Back-to-Back Enable. FB2B-Transaktionen zu verschiedenen (1), oder nur zum gleichen (0) Agent erlaubt.
CAS.PCISTS, PCI Status, Offset 06H, Word, [R*]: +++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |f|e|d|c|b|a 9|8|7|6|5|4|3 2 1 0| | | | | | | | | | | | | | |P|S| | | | D | |F| | | | | |E|E|R|R|S| E | |B|U| |N| | |R|R|M|T|T| V |D|2|D|6|C| | |R|R|A|A|A| T |P|B|F|6|L| | |S|S|S|S|S| S |D|C|S|C|P|0 0 0 0| +-+-+-+-+-+---+-+-+-+-+-+-+-+-+-+
HINWEIS: Die erlaubten Zugriffsarten für Bits dieses Feldes sind devicespe- zifisch. Größtenteils handelt es sich hier um [R/WC]-Bits, aber je nach Device können Bits auch auf 0 oder 1 gehardwired [R-] sein.
NCLP = New Capabilities List present. Dieses Bit ist gesetzt, falls ein Device eine entsprechende Liste bereithält (Adresszeiger auf diese Liste in Offset 14H bzw. 34H bei CAS.HEADT.TYPE = 2 bzw. = 0).
Meines Wissens existiert eine solche Liste nur bei Devices mit PCI Power Managment. Da diese Art Device (noch) relativ selten vorkom- mt, sei mir vorerst lediglich ein Verweis auf andere Quellen, wie z.B. Ralf Browns Interrupt List (siehe QLL0001) gestattet... &)
66C = 66MHz capable. Dieses Bit ist gesetzt, falls das Device mit einer 2.10+ Geschwindigkeit von 66 MHz am PCI-Bus betrieben werden kann. Bei gelöschtem Bit kann das Device nur mit 33 MHz (Standard-PCI), oder noch niedrigerer Frequenz betrieben werden.
UDFS = User defined format Support. Dieses Bit ist gesetzt, falls ein De- 2.10+ vice verschiedene Konfigurationen für verschiedene Umgebungen zur Verfügung stellt (Formate dann definiert über .PCF Files).
FB2BC = Fast Back-to-Back capable. Dieses Bit ist immer dann gesetzt, wenn das Device dazu fähig ist, FB2B-Transaktionen mit anderen Devices durchzuführen.
DPD = Data Parity detected. Dieses Bit wird immer dann gesetzt, wenn das Device einen Data Parity Error entdeckt.
DEVTS = DEVSEL# Timing Status. Geschwindigkeit, mit der das Device DEVSEL# Signale erzeugt, wenn es als Target antwortet:
00 fast 01 medium (Default für nahezu alle gängigen Devices) 10 slow 11 reserviert
HINWEIS: Configuration Cycles (Read und Write) sind nicht an diese Timings gebunden.
STAS = Signaled Target Abort Status. Ist ein Device als Target aktiv und muß es eine Transaktion zu einem Master unplanmäßig abbrechen, muß es dieses Bit setzen, um einen Target Abort anzuzeigen.
RTAS = Received Target Abort Status. Ist ein Device als Bus Master aktiv, setzt es dieses Bit, falls ein Target einen Master Cycle mit einem Target Abort beendet.
RMAS = Received Master Abort Status. Dieses Bit wird immer dann gesetzt, wenn das Device einen Master Cycle mit einem Master Abort beendet.
HINWEIS: Vorsicht, ein Master Abort ist auch der normale Abschluss von Special Cycles. RMAS = 1 muß also nicht zwangsläufig auch immer ein Fehler sein... &)
SERRS = SERR# Status. Dieses Bit wird immer dann gesetzt, falls das Device einen Address Parity Error bemerkt.
PERRS = Parity Error Status. Dieses Bit wird immer dann gesetzt, wenn in der Data oder Address Phase ein Paritätsfehler auftritt.
CAS.RID, RevisionID, Offset 08H, Byte, [R-]: ++++++++++++++++++++++++++++++++++++++++++++
Revisions-Identifikationsnummer (siehe dazu auch Datei PCI_DEV.LST).
CAS.CLASSC, Class Code, Offset 09H, 3 Byte, [R*]: +++++++++++++++++++++++++++++++++++++++++++++++++
Diese 3 Bytes, auch BCC (Base Class Code, Offset 0bH), SCC (Sub-Class Code, Offset 0aH) und PIF (Programming Interface, Offset 09H) genannt, ermöglichen es, PCI Devices einer bestimmten Hardwaregruppe zuzuordnen:
+------- BCC (Base Class Code, Offset 0bH, [R-]) | +----- SCC (Sub-Class Code, Offset 0aH, [R-]) | | +--- PIF (Programming Interface, Offset 09H, [R*]) ++++++ 00????H reserviert
HINWEIS: viele Devices aus den PCI-Anfangstagen (z.B. alte VGAs) melden 00????H, weil zum Fertigungszeitpunkt noch eine vorläufige (Draft) PCI-Spezifikation befolgt wurde.
01????H Mass Storage 010000H SCSI Controller 010100H (E)IDE Controller 010200H Floppy Controller (NEC 765 kompatibel) 010300H IPI Controller 010400H RAID Controller (ab 2.10+) 018000H andere
02????H Network Interface Controller 020000H Ethernet 020100H Token Ring 020200H FDDI 020300H ATM (ab 2.10+) 028000H andere
03????H Display Controller 030000H VGA (Hinweis: so melden sich auch einige ältere SVGAs!) 030100H SVGA 030101H 8514 kompatibel 030200H XGA 038000H andere
04????H Multimedia Controller 040000H Video 040100H Audio 048000H andere
05????H Memory 050000H RAM 050100H Flash Memory 058000H andere
06????H Bridge Controller 060000H Host Processor Bridge 060100H ISA Bridge 060200H EISA Bridge 060300H MCA Bridge 060400H PCI Bridge 060500H PCMCIA Bridge 060600H NuBus Bridge (ab 2.10+) 060700H CardBus Bridge (ab 2.10+) 068000H andere
--- 2.10+ ----------------------------------------------------------------
07????H Communication Device 070000H Serial Port (XT kompatibel) 070001H Serial Port (16450 kompatibel) 070002H Serial Port (16550 kompatibel) 070100H Parallel Port (XT kompatibel) 070101H Parallel Port (Bidirectional) 070102H Parallel Port (ECP 1.0+ kompatibel) 078000H andere
08????H System Peripherals Controller 080000H Generic PIC (8259 kompatibel) 080001H ISA PIC 080002H EISA PIC 080100H Generic DMA (8237 kompatibel) 080101H ISA DMA 080102H EISA DMA 080200H Generic PIT (8254 kompatibel) 080201H ISA PIT 080202H EISA PITs (2x PIT) 080300H Generic RTC 080301H ISA RTC 088000H andere
09????H Input Device 090000H Keyboard Controller 090100H Digitizer (Pen) 090200H Mouse 098000H andere
0a????H Docking Station 0a0000H Generic Docking Station 0a8000H andere
0b????H Processor 0b0000H 386 (& Kompatible) 0b0100H 486 (& Kompatible) 0b0200H Pentium (& Kompatible) 0b0300H Intel Pentium Pro 0b1000H DEC Alpha 0b2000H PowerPC 0b4000H Coprocessor 0b8000H andere
0c????H Serial Bus Controller 0c0000H Firewire (IEEE 1394) 0c0100H ACCESS bus 0c0200H SSA 0c0300H USB 0c0400H Fibre Channel 0c8000H andere
Rest: reserviert
CAS.CLS, Cache Line Size, Offset 0cH, Byte, [R-]: +++++++++++++++++++++++++++++++++++++++++++++++++
Wird für Memory Write and Invalidate Commands benötigt.
CAS.MLT, Master Latency Timer, Offset 0dH, Byte, [R*]: ++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dieses Feld bestimmt, für wieviele PCI-Clocks ein Device (vorausgesetzt, es handelt sich um einen Bus Master) bei Burst Transaktionen den Bus garantiert für sich beansprucht.
Der Wert von CAS.MLT dient bei jeder Transaktion, Burst oder Non-Burst, als Initialwert für den internen Downcounter des Devices. Entzieht der System Arbiter dem Device während einer Transaktion die zuvor erteilte Zugriffsbe- willigung auf den Bus, bricht das Device nur Non-Burst Transaktionen sofort ab. Bei Burst Transaktionen gibt das Device den Bus erst wieder frei, sobald der interne Downcounter den Wert Null erreicht hat.
Die Bits 0..2 von MLT sind in der Regel auf 0 gehardwired. In solchen Fällen kann der MLT zwangsläufig natürlich nur auf Vielfache von 8 PCI-Clocks pro- grammiert werden (so er denn überhaupt programmiert werden darf).
Die zeitliche Länge einer PCI-Clock beträgt bei Standard-PCI (33 MHz) 250ns.
CAS.HEADT, Header Type, Offset 0eH, Byte, [R-]: +++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6 5 4 3 2 1 0| | | | |M| | |F| | |D| TYPE | +-+-------------+
TYPE = Dieses Feld bestimmt das Headerformat von CAS-Bereich 10H..3fH:
00H Non-Bridge 01H PCI-to-PCI Bridge 02H PCI-to-CardBus Bridge
MFD = Multi (1) oder Single (0) Function Device
CAS.BIST, Built In Self Test, Offset 0fH, Byte: +++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7|6|5 4|3 2 1 0| | | | | | | |S| | | | |T| | | |S|A| | | |U|R| | C | |P|T|? ?| C | +-+-+-+-+-------+
CC [R-] = BIST Completion Code (0 = ok, 1..15 devicespezifisch) START [RW] = Start BIST. Starten durch Setzen des Bits (löscht sich selbst) SUP [R-] = Supported. BIST wird unterstützt (1) oder nicht (0).
HINWEIS: Bei Devices, die keinen BIST unterstützen, sind i.d.R. alle Bits auf 0 gehardwired. In solchen Fällen ist das ganze Feld natürlich nur ReadOnly [R-].
--- Headertype 00H (Non-Bridge) --------------------------------------------
CAS.H0.BASE0, Base Address 0, Offset 10H, DWord, [RW]: ++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dieses DWord funktioniert je nach Zustand von Bit 0 [R-] entweder als Memory Base Address (Bit 0 = 0), oder als I/O Base Address (Bit 0 = 1).
HINWEIS: Bei OpenHCIs (BCC = 0cH, Universal Serial Bus) zeigt dieses DWord generell auf die Base Address der Host Controller Register und hat in diesem Fall somit eine gänzlich andere Funktion als die hier be- schriebene. Für die Dokumentation dieser Register sei aber auf ent- sprechende andere Quellen verwiesen (ist einfach zuviel)...
Memory Base Address (Bit 0 = 0): +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4|3|2 1|0| | | | | | | |P| | | | |F| D | | | MSBs der Memory Base Address |S| P |0| +-------------------------------------------------------+-+---+-+
DP = Device Position:
00 irgendwo im 32bit-Adressraum 01 irgendwo im 16:16-Adressraum 10 irgendwo im 64bit-Adressraum 11 reserviert
HINWEIS: Falls DP = 10 ist, befinden sich die Bits 63..32 der Base Address im jeweils nächsten DWord (CAS.H0.BASEn).
PFS = Prefetch Support. Device unterstützt Prefetching (1) oder nicht (0).
I/O Base Address (Bit 0 = 1): +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1| | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0|f e d c b a 9 8 7 6 5 4 3 2|1|0| | | | | | | | | | | | | | | | | reserviert | MSBs der I/O Base Address |0|1| +-------------------------------+---------------------------+-+-+
CAS.H0.BASE1, Base Address 1, Offset 14H, DWord, [RW]: ++++++++++++++++++++++++++++++++++++++++++++++++++++++
Falls es sich beim vorangegangenen DWord (BASE0) um eine Memory Base Address im 64bit-Addressraum halten sollte (BASE0.DP = 10b), enthält BASE1 die Bits 63..32 dieser BASE0 Memory Base Address, anderenfalls hat BASE1 die gleiche Funktion, wie unter CAS.H0.BASE0 bereits beschrieben.
CAS.H0.BASE2, Base Address 2, Offset 18H, DWord, [RW]: ++++++++++++++++++++++++++++++++++++++++++++++++++++++
Falls es sich beim vorangegangenen DWord (BASE1) um eine Memory Base Address im 64bit-Addressraum halten sollte (BASE1.DP = 10b), enthält BASE2 die Bits 63..32 dieser BASE1 Memory Base Address, anderenfalls hat BASE2 die gleiche Funktion, wie unter CAS.H0.BASE0 bereits beschrieben.
CAS.H0.BASE3, Base Address 3, Offset 1cH, DWord, [RW]: ++++++++++++++++++++++++++++++++++++++++++++++++++++++
Falls es sich beim vorangegangenen DWord (BASE2) um eine Memory Base Address im 64bit-Addressraum halten sollte (BASE2.DP = 10b), enthält BASE3 die Bits 63..32 dieser BASE2 Memory Base Address, anderenfalls hat BASE3 die gleiche Funktion, wie unter CAS.H0.BASE0 bereits beschrieben.
CAS.H0.BASE4, Base Address 4, Offset 20H, DWord, [RW]: ++++++++++++++++++++++++++++++++++++++++++++++++++++++
Falls es sich beim vorangegangenen DWord (BASE3) um eine Memory Base Address im 64bit-Addressraum halten sollte (BASE3.DP = 10b), enthält BASE4 die Bits 63..32 dieser BASE3 Memory Base Address, anderenfalls hat BASE4 die gleiche Funktion, wie unter CAS.H0.BASE0 bereits beschrieben.
CAS.H0.BASE5, Base Address 5, Offset 24H, DWord, [RW]: ++++++++++++++++++++++++++++++++++++++++++++++++++++++
Falls es sich beim vorangegangenen DWord (BASE4) um eine Memory Base Address im 64bit-Addressraum halten sollte (BASE4.DP = 10b), enthält BASE5 die Bits 63..32 dieser BASE4 Memory Base Address, anderenfalls hat BASE5 die gleiche Funktion, wie unter CAS.H0.BASE0 bereits beschrieben.
CAS.H0.CBCISP, CardBus CIS Pointer, Offset 28H, DWord, [R-], 2.10+: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1|1 1 1 1 1 1 1 1 1 1 1 1 1 | | |f e d|c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2|1 0| | | | | | R | | | | I | | A | | N | OWAS | S | +-----+-----------------------------------------------------+---+
AS = Address Space:
000 OWAS relativ zum devicespezifischen CAS (ab Offset 40H) 001 OWAS relativ zu Pointer aus CAS.H0.BASE0 010 OWAS relativ zu Pointer aus CAS.H0.BASE1 011 OWAS relativ zu Pointer aus CAS.H0.BASE2 100 OWAS relativ zu Pointer aus CAS.H0.BASE3 101 OWAS relativ zu Pointer aus CAS.H0.BASE4 110 OWAS relativ zu Pointer aus CAS.H0.BASE5 111 OWAS relativ zu Pointer aus CAS.H0.XROMBASE
OWAS = Offset within address space. Die CIS (Card Information Structure) befindet sich an Offset OWAS relativ zum durch Bits 1..0 definierten Adressraum (siehe vorstehendes Feld AS).
RIN = ROM Image Number (?)
CAS.H0.SSVID, Subsystem VendorID, Offset 2cH, Word, [RW], 2.10+: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Subsystemhersteller-Identifikationsnummer.
CAS.H0.SSID, SubsystemID, Offset 2eH, Word, [RW], 2.10+: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Subsystem-Identifikationsnummer.
CAS.H0.XROMBASE, Expansion ROM Base Address, Offset 30H, DWord, [RW]: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | |f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b|a 9 8 7 6 5 4 3 2 1|0| | | | | | | |A| | | |D| | MSBs der ROM-Adresse | reserviert |E| +-----------------------------------------+-------------------+-+
ADE = Address Decode Enable. ROM aktiv(ieren) (1) oder nicht (0).
HINWEIS: Um Zugriffe auf ein Expansion ROM zuzulassen, muß zusätzlich zu ADE auch das CAS.PCICMD.MSE-Bit (04H[1]) gesetzt sein.
CAS.H0.OFSNCL, Offset to New Capabilities List, Offset 34H, DWord, [R-]: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Falls das CAS.PCISTS.NCLP-Bit (06H[4]) gesetzt ist, befindet sich hier der Offset zum ersten Eintrag der New Capabilities List. Da diese Liste meines Wissens nur bei Devices mit PCI Power Managment existiert und solche (noch) relativ gering verbreitet sind, möchte ich voerst noch auf entsprechende an- dere Quellen (z.B. Ralf Browns Interrupt List, s. QLL0001) verweisen... &)
CAS.H0.PAD, Offset 38H, DWord: ++++++++++++++++++++++++++++++
reserviert.
CAS.H0.IRQL, IRQ Line, Offset 3cH, Byte, [RW]: ++++++++++++++++++++++++++++++++++++++++++++++
+-+-+-+-+-+-+-+-+ |7 6 5 4|3 2 1 0| | | | | | N | | | U | | res. | M | +-------+-------+
NUM = Durch Device belegte (i.d.R. beim POST zugewiesene) IRQ-Line:
00H keine 01H IRQ1 : 0fH IRQ15
CAS.H0.IRQP, IRQ Pin, Offset 3dH, Byte, [R-]: +++++++++++++++++++++++++++++++++++++++++++++ +-+-+-+-+-+-+-+-+ |7 6 5 4|3 2 1 0| | | | | | N | | | U | | res. | M | +-------+-------+
NUM = Vom Device belegter IRQ-Pin:
00H keiner 01H INTA# 02H INTB# 03H INTC# 04H INTD#
CAS.H0.MINGRANT, Minimum Granted, Offset 3eH, Byte, [R-]: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minimale Zeit in PCI-Clocks (250ns bei 33 MHz PCI), für die das Device bei Burst Transaktionen die Kontrolle über den PCI Bus benötigt, wenn es als Bus Master arbeitet.
CAS.H0.MAXLAT, Maximum Latency, Offset 3fH, Byte, [R-]: +++++++++++++++++++++++++++++++++++++++++++++++++++++++
Maximale Zeit in PCI-Clocks (250ns bei 33 MHz PCI), für die das Device auf dem PCI Bus verweilen kann (wenn es als Bus Master arbeitet), bevor man mit deviceinternen Timingproblemen rechnen muß.
Das war's. Der CAS-Aufbau ab Offset 40H ist rein devicespezifisch und daher möchte ich hier nicht näher darauf eingehen, sondern auf die Dokumentation des jeweiligen PCI Device Herstellers verweisen.