
 ============================================================================
 Assembler FAQ (ASM86FAQ)                                   Stand: 26.10.1996
 ============================================================================

 Die in dieser FAQ enthaltenen  Dokumentationen und Quelltexte  dienen primr
 der Entlastung des Fido-Echos ASM86.GER.

 Hobby-Programmierern  sowie  Freeware,  Public Domain und  Shareware Autoren
 ist die Verwendung der FAQ-Quelltexte in eigenen Produktionen freigestellt.

 Die in dieser FAQ  aufgefhrten Texte  erheben keinen Anspruch  auf absolute
 Vollstndigkeit oder Fehlerfreiheit. Die Autoren bernehmen weder irgendeine
 Haftung fr die Eignung der Texte und Quelltexte zu einem  besonderen Zweck,
 noch fr Schden, die direkt, indirekt oder unabsichtlich durch den Einsatz,
 oder die Unfhigkeit sie einzusetzen, auftreten knnten. Benutzung also aus-
 schlielich auf eigene Gefahr.

 Wem das nicht passt, mge die FAQ keinesfalls einsetzen.



 === LEGENDE ================================================================

 ADD  Assembler/Disassembler/Debugger
 ALG  Algorithmen
 BAW  Basiswissen
 FRM  Formate
 LSG  Lsungen
 OPT  Optimierung
 QLL  Quellen
 SEC  Security
 TSR  TSR-Programmierung

 ?    Topic offen
 &    Topic in Arbeit
 +    Topic neu seit letzter FAQ
 !    Topic erweitert/korrigiert seit letzter FAQ



 === INHALTSVERZEICHNIS =====================================================

   BAW0001  Was ist Maschinensprache?
   BAW0002  Was ist Assembler?
   BAW0003  Warum Assembler?
   BAW0004  Zahlensysteme
   BAW0005  Datengren
   BAW0006  Datentypen
   BAW0007  CPU-Registersatz
   BAW0008  CPU-Befehlsformat
 & BAW0009  CPU-Befehlssatz
   BAW0010  MMX-Befehlssatz
 & BAW0011  FPU-Befehlssatz
 + BAW0012  Real Mode
 ? BAW0013  Protected Mode
 ? BAW0014  Virtual 86 (V86) Mode
 + BAW0015  RM/Flat4G Mode
 ? BAW0016  Interrupts
   BAW0017  Exceptions
 & BAW0018  Hochsprachen-Schnittstellen
 & BAW0019  VGA-Registersatz

 & ALG0001  DWord -> DezStr (Zahlenkonvertierung)
 & ALG0002  DWord -> HexStr (Zahlenkonvertierung)
   ALG0003  DWord -> BinStr (Zahlenkonvertierung)
 & ALG0004  DezStr -> DWord (Zahlenkonvertierung)
 & ALG0005  HexStr -> DWord (Zahlenkonvertierung)
 & ALG0006  BinStr -> DWord (Zahlenkonvertierung)
 & ALG0007  InsertionSort (Sortierung)
 & ALG0008  BubbleSort (Sortierung)
 & ALG0009  QuickSort (Sortierung)
 & ALG0010  MergeSort (Sortierung)
 & ALG0011  HeapSort (Sortierung)
 & ALG0012  ShellSort (Sortierung)
 & ALG0013  BucketSort (Sortierung)
 ? ALG0014  Boyer-Moore (Suche)
 ? ALG0015  Lempel-Ziv [LZ] (Komprimierung)
 ? ALG0016  Lempel-Ziv-Welch [LZW] (Komprimierung)
 ? ALG0017  Bresenham Line (Grafik)
   ALG0018  Bresenham Circle (Grafik)
 & ALG0019  Random (Zufallsverfahren)
 ? ALG0020  CRC (Prfsummenbildung)
 ? ALG0021  CRC32 (Prfsummenbildung)

 & FRM0001  PCX-Format (Grafik)
 ? FRM0002  MOD-Format (Musik)
 & FRM0003  FLI-Format (Animation)
 & FRM0004  FLC-Format (Animation)

   LSG0001  Wie erfahre ich, welche CPU vorhanden ist?
   LSG0002  Wie erfahre ich, welche FPU vorhanden ist?
   LSG0003  Wo liegen die Kommandozeilenparameter meines Programmes?
   LSG0004  Wie komme ich an das Startverzeichnis meines Programms?
 + LSG0005  Grundgerst fr RM/Flat4G und XMS
 + LSG0006  Grundgerst fr RM/Flat4G und RAW

   OPT0001  Grundstzliches zur Optimierung
   OPT0002  Code Alignment
   OPT0003  Data Alignment
   OPT0004  Register-Optimierung
 ? OPT0005  Unrolled Loops
   OPT0006  Cache-Optimierung (alle CPUs)
   OPT0007  386er-Optimierung
   OPT0008  Diverse Optimierungen
 ? OPT0009  Pairing

 & TSR0001  Grundstzliches zur TSR-Programmierung

   SEC0001  Anti-Debugger-Tips
   SEC0002  Verschlsseln von Daten
 ? SEC0003  Key Generation

 ? ADD0001  Assembler Pakete
 ? ADD0002  Disassembler Pakete
 ? ADD0003  Debugger Pakete
   ADD0004  MASM: Patch von 6.11 auf 6.11a

   QLL0001  Allgemeine Systemprogrammierung
   QLL0002  Allgemeine Algorithmen
   QLL0003  Allgemeine Grafikprogrammierung
   QLL0004  Assemblerpakete
   QLL0005  Programmieren in Assembler

   XXXXXXX  Abkrzungen



 === FAQ-spezifisches =======================================================

 Die FAQ gliedert sich momentan in neun Kategorien:

   BAW - Basiswissen

     Wissenswertes, bersichten (Registersatz, Befehlssatz, Taktzyklen u..).

   ALG - Algorithmen

     Erluterung verschiedener Standardalgorithmen (Quicksort u..). Sinn ist
     Herleitung,  Arbeitsschritte und  Ziel eines Algorithmus verstndlich zu
     machen.

   FRM - Formate und Strukturen

     Hufig verwendete Standardformate (z.B. PCX)  und  Systemstrukturen (wie
     (z.B. PSP, MCB usw.).

   LSG - Lsungen

     Quell-/Pseudocodes und/oder Erklrungen zur Lsung spezifischer Problem-
     stellungen.

   OPT - Optimierung

     Tips und Tricks zur Optimierung von Quellcodes.

   TSR - TSR-Programmierung

     Wissenswertes zum Dauerthema TSR-Programmierung.

   SEC - Security

     Programm- und Datenschutz, Shareware Keysysteme.

   ADD - Assembler/Disassembler/Debugger

     Allgemeines und Besonderheiten zu gngigen Assemblern, Disassemblern und
     Debuggern.

   QLL - Quellen

     In diesem Teil befinden sich Verweise  auf ntzliche Informationsquellen
     auerhalb der FAQ. Dabei handelt es sich in der Regel um FTP/WWW-Server,
     Dateien, Bcher, einschlgige Fachzeitschriften usw.

   XXX - Glossar

     Beschreibungen hufig anzutreffender Abkrzungen.

 Jedes Topic ist mit dem entsprechenden Gruppenkrzel und einer fortlaufenden
 Nummer  im Inhaltsverzeichnis  aufgefhrt (LSG0001).  Wird die FAQ  in einen
 Editor mit kontextsensitiver Suchfunktion geladen,  ist so das Auffinden des
 entsprechenden Topics wesentlich bequemer.

 In der Spalte vor der Topicnummer kann sich zudem eines der Zeichen ?, &, +,
 oder ! befinden:

 ? bedeutet, da ein bestimmtes Topic angefordert wurde,  es sich aber bisher
 kein Autor finden liess,  der in der Lage wre und die Zeit htte, das Topic
 in Angriff zu nehmen.  Interessenten mgen sich bitte per NM  (notfalls auch
 per Echomail in die ASM86.GER) an den FAQ-Keeper wenden (Adresse s. unten).

 & bedeutet, da am entsprechenden Topic bereits gearbeitet wird,  die Arbeit
 aber noch nicht abgeschlossen ist.

 + bedeutet, da dieses Topic  seit der letzten FAQ-Version neu hinzugekommen
 ist.

 ! bedeutet, da der Inhalt dieses Topics  seit der letzten FAQ-Version ber-
 arbeitet, erweitert, oder in sonstiger Form inhaltlich verndert wurde.

 Neue Lsungsanstze,  Verbesserungsvorschlge usw.  fr  bereits  bestehende
 Topics bitte an den jeweiligen Autor, oder an den FAQ-Keeper (s. unten).



 Informationen fr Topic-Keeper (und solche, die es werden mchten)
 ------------------------------------------------------------------

    Denkt bitte daran,  da sich der Status eines Topics immer auf den Zeit-
     punkt der letzten aktuellen Ausgabe bezieht. Es kann also durchaus sein,
     da jemand  offene Topics  zwischenzeitlich bereits unter seine Fittiche
     genommen hat.

     Kontaktiert daher bitte  zuerst den FAQ-Keeper (Adresse s. unten), bevor
     Ihr Euch spontan an die Arbeit macht... ;)

    Besttigte Topic- und Sub-Keeper:

     Bitte lasst Eure Textzeilen mit einem Leerzeichen beginnen,  dem maximal
     76 Textzeichen folgen.  Dadurch werden Eure Topics fr Personen, die mit
     einem Rahmen-Editor arbeiten,  wesentlich leichter lesbar und mir bleibt
     ein Haufen Formatierarbeit erspart.

     Die Krnung sind natrlich Abstze im Blockformat... ;)

     Deutsche Umlaute sind erwnscht (deutschsprachige FAQ).

     Zusendung von Topics  bitte vorzugsweise per NM,  um das Echo  nicht un-
     ntig zu belasten.

     Sollte sich Eure Fido-AKA ndern, bitte ich (FAQ-Keeper) mglichst frh-
     zeitig um Benachrichtigung,  um die nderung noch rechtzeitig in die FAQ
     einbauen zu knnen.

     "Redaktionsschlu" ;)  fr alle Topic-Einsendungen (egal ob neue, korri-
     gierte, erweiterte) ist immer der 25ste vor der nchsten FAQ-Ausgabe.



 Kontakt, Erscheinungsturnus, Bezug
 ----------------------------------

 FAQ-Keeper:

 Jrgen Thelen

 Fido:     2:2450/645.5
 Internet: j.thelen@lr.sunshine.de


 Jeweils zu Beginn eines Monats  erscheint immer die neueste Ausgabe der FAQ.

 Die FAQ kann  bei verschiedenen  Mailboxen frequestet  und in  verschiedenen
 Homepages eingesehen werden  (die neueste Adressbersicht  wird monatlich im
 Fido-Echo ASM86.GER durch die Mail "ASM86FAQ: Where?" wiedergegeben).



 Danksagung
 ----------

 Ich mchte allen Personen danken,  die mich bisher aktiv oder passiv bei der
 Erstellung und Erweiterung der FAQ untersttzt haben:

   (Brodmann, Andreas)
   Brown, Ralf
   Collins, Robert
   Deutschmann, Mario
   Dsterhft, Matthias
   Feldmann, Mark
   Frost, Eike
   Gehring, Jochen
   Gohel, Thomas
   (Grtzbach, Lennart)
   Heinen, Thomas-Ivo
   Huth, Michael
   Junker, Mark
   Klose, Michael
   Krmer, Horst
   Krieger, Markus
   Kubitz, Jrg
   Ludloff, Christian
   Meisel, Stefan
   Mller, Jochen
   Moon, Raymond
   Reelsen, Stefan
   Thoegersen, Finn
   Walter, Stefan
   Zapf, Bastiaan



 === BAW0001 ================================================================

 Was ist Maschinensprache?

 Juergen Thelen 2:2450/645.5:

 Maschinensprache ist von der Programmiererseite aus  die tiefste Ebene,  auf
 der mit einem Prozessor kommuniziert werden kann.

 Die Kommunikation erfolgt anhand binr kodierter Befehle (Opcodes),  die auf
 auf elektrischem/elektronischem Weg an den Prozessor bermittelt werden.

 Der Prozessor  dekodiert den Binrbefehl  und fhrt  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 lschen.

 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, mssen jeden Quelltext jedoch immer erst zurck in Maschinensprache
 bersetzen, damit ein Prozessor sie verarbeiten bzw. ausfhren kann.



 === BAW0002 ================================================================

 Was ist Assembler?

 Juergen Thelen 2:2450/645.5:

 Bekanntlich  verstehen  Prozessoren  nur  Maschinensprache. Maschinensprache
 besteht nur aus Opcodes,  also Bytes oder Bytefolgen, deren binre Kodierung
 eine festgelegte Aktion im Prozessor auslst.

 Allerdings existieren nun, je nach Prozessor,  Hunderte verschiedene solcher
 Opcodes.  Diese Opcodes alle auswendig kennen bzw. Opcode-Tabellen wlzen zu
 mssen, um berhaupt programmieren zu knnen, ist natrlich nicht gerade als
 angenehme Form der Programmierung zu bezeichnen.

 Die Sprache Assembler beseitigt diesen Nachteil, indem sie dem Programmierer
 ermglicht, anstelle von Opcodes kurze, einprgsame Worte zu benutzen. Diese
 Worte werden als Mnemonics bezeichnet.

 Beispiel:

 Opcode             Mnemonic

 f8H (11111000b)    CLC (fr CLear Carry)

 Um ein Programm  in Assembler  entwickeln zu knnen,  bentigt man nun einen
 Assembler. Das klingt paradox, aber mit letzterem Assembler ist ein Programm
 gemeint, da einen in Assembler (der Sprache) geschriebenen Quelltext wieder
 zurck in Maschinensprache (Opcodes) bersetzen kann.

 Hochsprachenanwendern bietet sich diese Konfusion nicht,  dort nennt man die
 Rckbersetzung eines Quelltextes Kompilieren,  es sei denn, es handelt sich
 um eine interpretierende Hochsprache (Rckbersetzung whrend der Laufzeit).



 === BAW0003 ================================================================

 Warum Assembler?

 Juergen Thelen 2:2450/645.5:

 Ich persnlich  verwende  Assembler  immer dann,  wenn sich etwas  mit einer
 Hochsprache entweder gar nicht,  oder nur zu inaktzeptablen Bedingungen rea-
 lisieren lsst. 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 wren,  Register gelscht,  die bereits gelscht sind,
    unntze 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 natrlich ab-
    hngig davon, welches Problem gelst 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 lsen wren.

 2. Ich brauche direkten Zugriff auf Prozessor- oder System-Interna,  der von
    der Hochsprache nicht,  oder nur auf Umwegen,  untersttzt 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 mchte 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... ;)



 === BAW0004 ================================================================

 Zahlensysteme

 Juergen Thelen 2:2450/645.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 (binren, 2) und
 hexadezimalen (16) System "gedacht".

 Es existiert aber kein groer 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 durchgezhlt
 werden kann, bevor eine weitere Stelle (10) erforderlich wird.

 Diese Logik gilt auch  fr die beiden  anderen Systeme.  Auch hier wird jede
 Stelle  von 0 an durchgezhlt,  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 durchgezhlt wird,  ist ja nachvollziehbar, aber wie sollen 10 bis 15
 in einer Stelle dargestellt werden? Die Lsung 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  binre und  hexadezimale System sind zu Beginn zwar etwas gewhnungsbe-
 drftig,  da sich im Unterbewutsein  immer wieder das  dezimale System nach
 vorne drngelt, aber mit der Zeit wird es zunehmend einfacher... ;)



 === BAW0005 ================================================================

 Datengren

 Juergen Thelen 2:2450/645.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 hauptschlich  mit den Datengren
 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, hchstwertiges Bit) be-
 zeichnet wird:


    Byte Ĵ

   Ŀ
   76543210
   


 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 Ĵ

   Ŀ
   fedcba9876543210
   

    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

             :               :

             Ŀ  Ŀ
             fedcba98    
      1                      
                   43h          
             Ĵ     Word (Little-Endian)
             76543210    
      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 Ĵ

   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    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

             :               :

             Ŀ  Ŀ
             11111111    
             fedcba98    
      3                      
                   87h          
             Ĵ    
             11111111    
             76543210    
      2                      
                   65h          
             Ĵ     Doubleword (Little-Endian)
             fedcba98    
      1                      
                   43h          
             Ĵ    
             76543210    
      0                      
                   21h          
               



 === BAW0006 ================================================================

 Datentypen

 Juergen Thelen 2:2450/645.5:

 Die Prozessoren der Intel 8086 / Pentium Families (& Kompatiblen)  interpre-
 tieren Operanden  in Abhngigkeit  der zugehrigen  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 tatschliche 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 LowNibble (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
   Lnge. 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
   hngt 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 hngen 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.



 === BAW0007 ================================================================

 CPU-Registersatz

 Juergen Thelen 2:2450/645.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 erlutern,  wrde 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



 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


 Ŀ
 1111111111111111                
 fedcba9876543210fedcba9876543210
 

                                  ?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+) knnen fr
 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 fr ganz bestimmte Operationen
 prdestiniert bzw. fest an sie gebunden, z.B.:

   CS:[E]IP      Zeiger auf die nchste auszufhrende Instruction
   SS:[E]SP      Zeiger auf das nchste 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
 knnen dem Topic BAW0009 (CPU-Befehlssatz) entnommen werden.



 Flag Register
 

                                  FLAGS Ĵ
  EFLAGS Ĵ   386+
 Ŀ
 1111111111111111                 
 fedcba9876543210fed cba9876543210
                                  
           C                      
           P                      
           UVV      I             
           III VR N O ODITSZ A P C
 0000000000DPF0MF0T P FFFFFF0F0F1F
 


  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
  DF    = Direction Flag
  OF    = Overflow
  IOP   = I/O Privilege Level             (286+)
  NT    = Nested Task Flag                (286+)
  RF    = Resume Flag                     (386+)
  VM    = Virtual Mode                    (386+)
  VIF   = Virtual Interrupt Flag          (P5+, neuere 486)
  VIP   = Virtual Interrupt Pending       (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+
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
                                   
   PCN          A W           ETEMP
   GDW0000000000M0P00000000000TSMPE
   

    0  = reserviert
    PE = Protection Enable   (286+)
    MP = Monitor Coprocessor (286+)
    EM = Emulation           (286+)
    TS = Task switched       (286+)
    ET = Extension Type      (286+)
    WP = Write Protect       (486+)
    AM = Align Mode          (486+)
    NW = No write            (486+)
    CD = Cache disable       (486+)
    PG = Paging              (386+)



   Control Register CR1
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    CR1 reserviert Ĵ


   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



   Control Register CR2
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    Linear adress Ĵ   386+


   CR2 wird zur Behandlung von Page Faults benutzt.  Wird das PG-Bit (Bit 31)
   in CR0 gesetzt,  speichert der Prozessor in CR2  die lineare Adresse,  die
   einen Page Fault (siehe BAW0017) verursacht hat.



   Control Register CR3
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    Page Directory Base Register  reserviert Ĵ   386+


   CR3 wird ebenfalls zur Behandlung von Page Faults verwendet.  Wird das PG-
   Bit (Bit 31) in CR0 gesetzt, legt der Prozessor das Page Table Verzeichnis
   des aktuellen Tasks in den Bits 12..31 von CR3 ab.  Bits 0..11 sind reser-
   viert.



   Control Register CR4
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
                                   
                          P MPP TPV   486+
                          C CGSDSVM
   00000000000000000000000E0EEEEDIE
   

    0   = reserviert
    VME = Virtual Mode Virtual Interrupt Enable     (486+)
    PVI = Protected Mode Virtual Interrupt Enable   (486+)
    TSD = Time Stamp Enable                         (P5+)
    DE  = Debugging Extension Enable                (P5+)
    PSE = Page Size Extension Enable                (P5+)
    PGE = Page Size Global Extension Enable         (P6+)
    MCE = Machine Check Exception Enable            (P5+)
    PCE = Enable RDPMC-Instruction for CPL0 only    (P6+)

   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



   Control Register CR5
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    CR5 reserviert Ĵ


   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



   Control Register CR6
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    CR6 reserviert Ĵ


   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



   Control Register CR7
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    CR7 reserviert Ĵ


   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



 Debug Register
 


   Debugregister DR0..DR3
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    Linear Adress Breakpoint 0 (DR0) Ĵ   386+
    Linear Adress Breakpoint 1 (DR1) Ĵ   386+
    Linear Adress Breakpoint 2 (DR2) Ĵ   386+
    Linear Adress Breakpoint 3 (DR3) Ĵ   386+


   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 zugehrigen Bits von DR7 die gewnschte Break-Condition.



   Debugregister DR4
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    DR4 reserviert Ĵ   386+


   Undokumentiert 386: Mirror/Alias fr DR6



   Debugregister DR5
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    DR5 reserviert Ĵ   386+


   Undokumentiert 386: Mirror/Alias fr DR7



   Debugregister DR6
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
                                      386+
                   BBB         BBBB
   0000000000000000TSD0000000003210
   

    0  = reserviert
    B0 = Breakpoint 0 triggered
    B1 = Breakpoint 1 triggered
    B2 = Breakpoint 2 triggered
    B3 = Breakpoint 3 triggered
    BD = Debug Break triggered
    BS = Single Step Break triggered (EFLAGS Trap)
    BT = Task Break triggered (TSS Debug Trap)


   Aus DR6 kann der Status seit dem letzten Breakpoint entnommen werden.

   HINWEIS:  Der Prozessor lscht niemals  DR6 aus eigenem Antrieb, dafr 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) erfllt ist.

   BD in DR6 wird nur gesetzt,  falls Intels ICE gerade die Debugregister DR0
   bis DR7 benutzt und die nchste Instruction  ebenfalls auf ein DR-Register
   zugreifen wrde.

   BS in DR6 wird gesetzt, falls  eine Single Step Exception ausgelst wurde,
   d.h. das TF-Bit (Bit 8) in FLAGS/EFLAGS gesetzt wurde/ist. Single Step hat
   hchste Debug-Prioritt, es knnten 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 11 11 11 11 11 11 11 1                
   f ed cb a9 87 65 43 21 0fedcba9876543210
                                           
    L  R  L  R  L  R  L  R                 386+
    E  /  E  /  E  /  E  /  III            
    N  W  N  W  N  W  N  W  BGI  GLGLGLGLGL
    3  3  2  2  1  1  0  0 0TDR00EE33221100
   

    0    = reserviert
    L0   = Local Enable Breakpoint 0
    L1   = Local Enable Breakpoint 1
    L2   = Local Enable Breakpoint 2
    L3   = Local Enable Breakpoint 3
    G0   = Global Enable Breakpoint 0
    G1   = Global Enable Breakpoint 1
    G2   = Global Enable Breakpoint 2
    G3   = Global Enable Breakpoint 3
    LE   = Local Exact Data Breakpoint Match
    GE   = Global Exact Data Breakpoint Match
    IIR  = ICE Interrupt Redirection (undokumentiert, 386, 486)
    IGD  = ICE Global Debug Enable (undokumentiert)
    IBT  = ICE Branch Trace Messages (undokumentiert)
    R/W0 = Break Condition Breakpoint 0
    R/W1 = Break Condition Breakpoint 1
    R/W2 = Break Condition Breakpoint 2
    R/W3 = Break Condition Breakpoint 3
    LEN0 = Length of monitored item for Breakpoint 0
    LEN1 = Length of monitored item for Breakpoint 1
    LEN2 = Length of monitored item for Breakpoint 2
    LEN3 = Length of monitored item for Breakpoint 3


   Im Zusammenhang mit den Registern DR0 bis DR3  knnen 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 auslst:

     00  nur bei Ausfhrung der Instruction an Adresse DRn
     01  nur bei Schreibzugriff auf Adresse DRn
     10  reserviert
     11  bei Schreib- oder Lesezugriff auf Adresse DRn

   LENn definiert die Operandengre 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 existieren NUR auf 386 und 486 CPUs. Ab P5 gibt es
            es keinen TLB (Transfer Lookaside Buffer) mehr,  also werden auch
            die Register TRn nutzlos...

   Beim TLB handelt es sich um einen Cache, ber den auf 386/486 CPUs 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
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    TR0 reserviert Ĵ


   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



   Test Register TR1
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    TR1 reserviert Ĵ


   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



   Test Register TR2
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
   

    TR2 reserviert Ĵ


   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



   Test Register TR3
   
   Ŀ
   1111111111111111                   486
   fedcba9876543210fedcba9876543210
   

    Internal Cache Ĵ


   Undokumentiert 386: Lst bei Verwendung Int 6 (Invalid) aus.



   Test Register TR4
   
   Ŀ
   1111111111111111                   486
   fedcba9876543210fedcba9876543210
   

    Internal Cache Ĵ


   Undokumentiert 386: Mirror/Alias TR5.  Aus diesem Register kann die 32bit-
                       Adresse ausgelesen werden,  die beim nchsten Prefetch
                       auf dem CPU-Bus erscheint. Durch Nachhalten der Anzahl
                       TR4-nderungen bei der Ausfhrung von Instructions ist
                       so die Gre bzw. Granularity der Prefetch-Unit ermit-
                       telbar. Sehr hilfreich fr 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 fr einen
                       ganz anderen Zweck eingesetzt wird.  Wen interessiert,
                       wie's trotzdem geht: x86.org [QLL0001]...



   Test Register TR5
   
   Ŀ
   1111111111111111                   486
   fedcba9876543210fedcba9876543210
   

    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 Ĵ  386/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 cba 98 76 543210
                                                         
                                            D  U  R      
                                            T  /  /     T
         Linear Address (20 MSBs)         V Y  S  W 0000C
   

    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 Ĵ  386/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 cba9876543 210
                                                       
                                                 H R   
                                                 I E   
        Physical Address (20 MSBs)        0000000T P 00
   

    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)



 === BAW0008 ================================================================

 CPU-Befehlsformat

 Juergen Thelen 2:2450/645.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
 

 Diese 1-Byte-Komponente  ist nur bei Verwendung von REP? und LOCK vorhanden.
 Das Byte hat in diesen Fllen einen der folgenden Werte:


   f0h   LOCK
   f2h   REPNE, REPNZ
   f3h   REP, REPE, REPZ


 Address-Size Prefix
 

 Im Real und V86 Mode  werden Adressangaben  in der Voreinstellung als 16 Bit
 Adressen behandelt. Im Protected Mode wird die Standardgre 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
 

 Die Voreinstellung der vom Prozessor benutzten Operandengre hngt von ver-
 schiedenen Faktoren,  wie z.B.  vom Instruction Prefix,  verschiedenen Stan-
 dardzuweisungen usw., ab.

 Diese Voreinstellungen  knnen (wo zulssig)  durch Einsatz des Operand-Size
 Prefix (1-Byte-Komponente) bergangen werden. Der Prefix hat folgenden Wert:


   66h   Operand-Size Override


 Folgende Tabelle  zeigt alle mglichen Kombinationen  von D-Bit im ESD,  dem
 Address- und dem Operand-Size Prefix, und deren Auswirkung:


    D-Bit im ESD             :  1 = gesetzt  , 0 = gelscht
     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
 

 Speziell  bei Datenzugriffen  werden vielen Befehlen  Standardsegmente zuge-
 wiesen.  Diese Voreinstellungen knnen  (wo zulssig)  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-
 fhren soll.

 Eine ausfhrliche Beschreibung aller Opcodes  befindet sich im Topic BAW0009
 (CPU-Befehlssatz).


 Mod R/M, SIB, Displacement, Immediate
 

 Hmmm,  ich wei nicht,  ob detaillierte Erluterungen  zu diesen Komponenten
 irgend jemand im Echo etwas bringen.

 Ich hab' mich  vor tausend Jahren  zwar mal  mit der Mod R/M Dekodierung bei
 8086/88ern rumgeschlagen, aber das geschah eher aus Langeweile... 8)

 Wirklichen Nutzen konnte ich aus den Informationen damals nicht ziehen...

 Was meint Ihr? Brauchen wir sowas in der FAQ?

 Wenn ja: Hat einer von Euch neuere Docs dazu (ich hab' nur uralte 8086/88er)
          oder bernimmt vielleicht sogar die Doku des Topics?



 === BAW0009 ================================================================

 CPU-Befehlssatz

 Juergen Thelen 2:2450/645.5:

 Dieses Topic beschftigt sich  mit Erluterungen zur Funktionsweise der ein-
 zelnen Befehle (Instructions), deren mgliche Adressierungsarten,  ihren Op-
 codes, beeinflussten Flags, Taktzyklenangaben, mglichen 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 gelscht    
     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 Ausfhrung derartiger Befehle lsst die CPU nur zu, wenn das
 ausfhrende Programm CPL0, d.h. den hchsten Privileglevel besitzt.

 Erscheint PMO ber dem Befehlskasten,  handelt es sich um einen Befehl,  den
 die CPU nur im Protected Mode erkennt und ausfhrt.

 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 mglichen Adressierungsarten  des jeweiligen
   Befehles aufgefhrt. Dabei werden folgende Abkrzungen 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 oder ESP
     m8      8bit-Speicheroperand fr String ?SB (DS:[E]SI oder ES:[E]DI)
     m16     16bit-Speicheroperand fr String ?SW (DS:[E]SI oder ES:[E]DI)
     m32     32bit-Speicheroperand fr String ?SD (DS:[E]SI oder ES:[E]DI)
     r|m8    8bit-Operand (Register oder Speicher)
     r|m16   16bit-Operand (Register oder Speicher)
     r|m32   32bit-Operand (Register oder Speicher)


 PIPE

   Diese Spalte  betrifft nur Pentium Prozessoren.  Pentium CPUs arbeiten mit
   einer Dual Pipe Architecture (DPA).  Die DPA ermglicht die parallele Aus-
   fhrung  zweier Befehle gleichzeitig,  das sogenannte Pairing.  Allerdings
   funktioniert das Pairing nicht ausnahmslos und ohne Einschrnkungen.

   Die Pairing-Voraussetzungen  sind sehr umfangreich und teilweise auch sehr
   spezifisch (Schlagwrter:  No double NP,  Reg Contentions (Flow-, Output-,
   Anti-Dependence), MemBank Conflicts (Data Cache), Inter-Pipe Concurrency).

   Das Pairing selbst zu erlutern ist nicht Sinn dieses Topics, daher finden
   Sie  in der Spalte  lediglich Angaben  zur grundstzlichen 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  werden die Anzahl Taktzyklen angegeben,  die ein Befehl
   auf dem jeweiligen Prozessor zur Ausfhrung bentigt.  Die zeitliche Lnge
   eines Taktzyklus hngt von der Taktfrequenz der CPU ab. Sie kann mit einer
   der folgenden Formeln ermittelt werden:


     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:


      Der Befehl  muss bereits  vollstndig decodiert zur Ausfhrung bereit-
       stehen.  Prefetching,  Cache Hits/Misses, Write-Strategien usw. wurden
       nicht bercksichtigt.

      Buszugriffe bentigen keine WAIT STATES.

      Es liegen keine lokalen HOLD Requests an.

      Speicheroperanden sind aligned.

      Durch die Ausfhrung des Befehles wird keine Exception ausgelst.


   Bei manchen Befehlen tauchen  in den Taktzyklenspalten zustzliche Abkrz-
   ungen auf:


     EA   Zusatzzyklen fr Effective Address Computation
     TS   Zusatzzyklen fr Task Switch (Exceptions)
     n    Anzahl Wiederholungen
     m    Anzahl Komponenten
     PM   Zyklenangabe gilt im Protected Mode
     VM   Zyklenangabe gilt im V86 Mode


   EA - Effective Address Computation

   Auf 8086ern  mu bei einigen Befehlen  bzw. deren Adressierungsarten, erst
   noch eine effektive Adresse  vor der Ausfhrung  berechnet werden.  Dieser
   Vorgang bentigt natrlich  einige zustzliche Taktzyklen.  Obwohl wir uns
   eigentlich im Zeitalter der P5er bewegen, der Vollstndigkeit 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

   Lst ein Befehl einen Task Switch  in ein anderes Segment aus, dann mssen
   natrlich auch entsprechende Zusatzzyklen miteinkalkuliert werden. Wieviel
   Zusatzzyklen dies sind,  hngt 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).


                                           Neuer Task

                      486 TSS  486 TSS VM  386 TSS  386 TSS VM  286 TSS
                                                               
     Alter Task                                                
                                                               
     486: 286 TSS       199       177        ---       ---       180
     
     386: 386 TSS       ---       ---        309       226       282
     
     386: 386 TSS VM    ---       ---        314       231       287
     
     386: 286 TSS       ---       ---        307       224       280
     


 OPCODE

   In dieser Spalte  finden Sie  die dem jeweiligen Befehl entsprechenden Op-
   codes (hex). Zustzlich werden folgende Abkrzungen verwendet:

     ib        Opcode, Mod R/M- oder SIB-Byte folgt eine 8bit-Konstante
     iw        Opcode, Mod R/M- oder SIB-Byte folgt eine 16bit-Konstante
     id        Opcode, Mod R/M- oder SIB-Byte folgt eine 32bit-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        Der Befehl verwendet einen R/M und einen Register-Operanden.


 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)
     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 hchsten 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  natrlich deftig
            verwirren, wenn man nicht immer dran denkt... ;)


 BESCHREIBUNG

   Ausfhrliche Beschreibung des Befehls und eventueller Besonderheiten.


 MGLICHE EXCEPTIONS

   Angaben zu mglichen Exceptions  bei der Anwendung des jeweiligen Befehles
   im Real Address (RM), Virtual 86 (VM) oder Protected Mode (PM).

   Ausfhrliche Beschreibungen  der einzelnen Exceptions  finden Sie im Topic
   BAW0017 (Exceptions).





                                                           O D I T S Z A P C
 Ŀ
  AAA - ASCII Adjust after Addition                      ? - - - ? ? * ? *
 

   Befehl             Pipe  P5  486    386    286    86        Opcode

   AAA                 NP   3   3      4      3      8         37


   Pseudocode:

     if ((AL AND 0fH) > 9) OR (AF = 1) then
       AL = (AL + 6) AND 0fH
       AH = AH + 1
       AF = 1
       CF = 1
     else
       AF = 0
       CF = 0
     endif


   Beschreibung:

     Addiert man  BCDs mittels ADD,  kann es zu einem Stellenberlauf kommen,
     der zu falschen BCD-Ergebnissen fhrt. Die Ursache hierfr ist keine de-
     fekte CPU ;), sondern die Tatsache,  da Intel x86 CPUs zwar binr, aber
     eben nicht binr 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 gefhrt. Betrachtet man sich jetzt das Lo-
     Nibble (?2H) des Ergebnisses,  fllt sofort auf, da da bei BCD-Betrach-
     tungsweise eigentlich (?8H) stehen msste, denn schlielich 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? Schlielich 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 erhlt...


   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 * 10) + AL
     AH = 0

   Beschreibung:

     Wie bei der Instruction AAA bereits beschrieben,  rechnen Intel x86 CPUs
     zwar binr, aber nicht binr codiert (BCD, Binary coded decimal).

     Daraus folgt, da eine BCD vor einer Division natrlich zuerst wieder in
     das "richtige" binre 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 mte = 2 sein.
     Genau deshalb wird hier AAD gebraucht:


       mov    ax, 0108H       ; BCD = 18
       aad                    ; AX  = 0012H = 18 (binr)
       mov    bl, 9
       div    bl              ; / 9
       ================
       ax     =   0002H       ; BCD = 02


     Zufllig geht diese  Division glatt auf (2, Rest 0).  Dadurch hat es den
     Anschein,  als ob auch die BCD-Zehnerstelle stimmen wrde.  Da dies nur
     Zufall ist, drfte wohl klar sein... ;)

   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 ;) lnger verwenden... 8)

     Wozu?  Nun, der 2. Opcode der AAD Instruction ist nichts anderes als der
     Multiplikator fr AH.  Mchte man AH mit einem anderen Wert als 0aH (10)
     multiplizieren, setzt man einfach den 2. Opcode entsprechend.

     Hinweis: "AAD imm8" eignet sich  natrlich nur  fr Ergebnisse  im 8bit-
              Bereich (< 256).

     Der Einsatz von "AAD imm8" bietet zwei Vorteile. Zum einen lsst sich so
     ein (bis heute leider nicht existentes) "MUL r8, imm8", zumindest einge-
     schrnkt, emulieren. Zum anderen gibt es bestimmte Situationen, in denen
     "AAD imm8" einfach schneller ist, als entsprechende MUL/ADD Sequenzen.


   Exceptions:

     RM   -
     VM   -
     PM   -


                                                           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:

     AH = AL / 10
     AL = AL mod 10

   Beschreibung:

     Wie bei der Instruction AAA bereits beschrieben,  rechnen Intel x86 CPUs
     zwar binr, aber nicht binr 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 binr korrekt = 81 (51H),  aber dessen BCD-Entsprechung wre
     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 gewnschte 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 lnger verwendet... ;)

     hnlich wie bei "AAD imm8"  ist auch bei "AAM imm8" der 2. Opcode in der
     Realitt ein Operand  (hier eben  der Divisor fr AL).  Wer also AL z.B.
     durch 8 statt 10 teilen mchte, benutzt einfach D4 08 statt D4 0a...

     Btw, sollte sich jemand darber wundern, wieso hier, entgegen anderslau-
     tenden (offiziellen) Dokumentationen,  OF, SF und CF als gleich 0 aufge-
     fhrt sind: Damit schliesse ich mich den imo logischen Schlussfolgerung-
     en von Robert Collins an. Robert erluterte  das mal in einem seiner Ar-
     tikel wie folgt (sinngemsse bersetzung):


       "In den offiziellen Intel-Dokumentationen wird behauptet, da bei Ein-
        satz des normalen AAM-Befehls (D4 0a) OF und CF nach der Ausfhrung
        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 mssen OF und CF = 0 sein."

       "Desweiteren wird in offiziellen Intel-Dokumentationen angegeben,  da
        das SF  der Operation entsprechend  gesetzt oder gelscht  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 knnen im Original (englisch) auf seiner Web-
     Site (siehe QLL0001) nachgelesen werden.


   Exceptions:

     RM   (nur bei D4 00: Int 0 (DivideError))
     VM   (nur bei D4 00: Int 0 (DivideError))
     PM   (nur bei D4 00: Int 0 (DivideError))


                                                           O D I T S Z A P C
 Ŀ
  AAS - ASCII Adjust after Subtraction                   ? - - - ? ? * ? *
 

   Befehl             Pipe  P5  486    386    286    86        Opcode

   AAS                 NP   3   3      4      3      8         3F


   Pseudocode:

     if ((AL AND 0fH) > 9) OR (AF = 1) then
       AL = (AL - 6) AND 0fH
       AH = AH - 1
       AF = 1
       CF = 1
     else
       AF = 0
       CF = 0
     endif

   Beschreibung:

     Wie bei der Instruction AAA bereits beschrieben,  rechnen Intel x86 CPUs
     zwar binr, aber nicht binr codiert (BCD, Binary coded decimal).

     Subtrahiert man BCDs mittels SUB, kann es zu einem Stellenunterlauf kom-
     men, der zu falschen BCD-Ergebnissen fhrt. 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      2      3      4         14 ib
   ADC AX, imm16       PU   ?   1      2      3      4         15 iw
   ADC EAX, imm32      PU   ?   1      2      -      -         15 id
   ADC r|m8, imm8      PU   ?   1|3    2|7    3|7    4|17+EA   80 /2 ib
   ADC r|m16, imm16    PU   ?   1|3    2|7    3|7    4|17+EA   81 /2 iw
   ADC r|m32, imm32    PU   ?   1|3    2|7    -      -         81 /2 id
   ADC r|m16, imm8     PU   ?   1|3    2|7    3|7    4|17+EA   83 /2 ib
   ADC r|m32, imm8     PU   ?   1|3    2|7    -      -         83 /2 ib
   ADC r|m8, r8        PU   ?   1|3    2|7    2|7    3|16+EA   10 /r
   ADC r|m16, r16      PU   ?   1|3    2|7    2|7    3|16+EA   11 /r
   ADC r|m32, r32      PU   ?   1|3    2|7    -      -         11 /r
   ADC r8, r|m8        PU   ?   1|2    2|6    2|7    3|9+EA    12 /r
   ADC r16, r|m16      PU   ?   1|2    2|6    2|7    3|9+EA    13 /r
   ADC r32, r|m32      PU   ?   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 Ausfhrung
     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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           O D I T S Z A P C
 Ŀ
  ADD - Add                                              * - - - * * * * *
 

   Befehl             Pipe  P5  486    386    286    86        Opcode

   ADD AL, imm8        UV   ?   1      2      3      4         04 ib
   ADD AX, imm16       UV   ?   1      2      3      4         05 iw
   ADD EAX, imm32      UV   ?   1      2      -      -         05 id
   ADD r|m8, imm8      UV   ?   1|3    2|7    3|7    4|17+EA   80 /0 ib
   ADD r|m16, imm16    UV   ?   1|3    2|7    3|7    4|17+EA   81 /0 iw
   ADD r|m32, imm32    UV   ?   1|3    2|7    -      -         81 /0 id
   ADD r|m16, imm8     UV   ?   1|3    2|7    3|7    4|17+EA   83 /0 ib
   ADD r|m32, imm8     UV   ?   1|3    2|7    -      -         83 /0 ib
   ADD r|m8, r8        UV   ?   1|3    2|7    2|7    3|16+EA   00 /r
   ADD r|m16, r16      UV   ?   1|3    2|7    2|7    3|16+EA   01 /r
   ADD r|m32, r32      UV   ?   1|3    2|7    -      -         01 /r
   ADD r8, r|m8        UV   ?   1|2    2|6    2|7    3|9+EA    02 /r
   ADD r16, r|m16      UV   ?   1|2    2|6    2|7    3|9+EA    03 /r
   ADD r32, r|m32      UV   ?   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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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      2      3      4         24 ib
   AND AX, imm16       UV   ?   1      2      3      4         25 iw
   AND EAX, imm32      UV   ?   1      2      -      -         25 id
   AND r|m8, imm8      UV   ?   1|3    2|7    3|7    4|17+EA   80 /4 ib
   AND r|m16, imm16    UV   ?   1|3    2|7    3|7    4|17+EA   81 /4 iw
   AND r|m32, imm32    UV   ?   1|3    2|7    -      -         81 /4 id
   AND r|m16, imm8     UV   ?   1|3    2|7    -      -         83 /4 ib
   AND r|m32, imm8     UV   ?   1|3    2|7    -      -         83 /4 ib
   AND r|m8, r8        UV   ?   1|3    2|7    2|7    3|16+EA   20 /r
   AND r|m16, r16      UV   ?   1|3    2|7    2|7    3|16+EA   21 /r
   AND r|m32, r32      UV   ?   1|3    2|7    -      -         21 /r
   AND r8, r|m8        UV   ?   1|2    2|6    2|7    3|9+EA    22 /r
   AND r16, r|m16      UV   ?   1|2    2|6    2|7    3|9+EA    23 /r
   AND r32, r|m32      UV   ?   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-
     Verknpfung  durchgefhrt.  Ergebnis der Verknpfung  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


   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code]
     PM   #SS[0], #GP[0], #PF[Code]


   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=    -         63 /r
                                9|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  (hheres Privileg) be-
     sitzt,  als das Betriebssystem selbst.  Eine entsprechende Prfung luft
     dann in etwa so ab:


        mov   ax, [BS_Selector]
        arpl  [Sub_Selector], ax


     Sollte der Unterroutinen-Selector  bei dieser Prfung  tatschlich hher
     privilegiert sein (Sub_Selector.RPL  <  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 zustzliche Manahmen erforderlich sein, kann der Pro-
     grammierer bei gesetztem ZF entsprechendes veranlassen, ohne selbst noch
     einen Vorher-/Nachher-Vergleich durchfhren zu mssen.


   Exceptions:

     RM   Int 6 (Invalid)
     VM   Int 6 (Invalid), #PF[Code]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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 (d.h. KEINE Auslsung von Int 05H)


   Pseudocode:

     if (DEST < SRC[LowerBound]) OR (DEST > SRC[UpperBound]) then
       INT 5
     endif

   Beschreibung:

     BOUND dient der Untersttzung von sogenannten Range-Checks.  Unter einem
     Range-Check versteht man eine Prfung eines Wertes daraufhin,  ob er so-
     wohl grer 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 wre der Range-Check erfolgreich. Wre AX z.B. = 300,  wrde
     BOUND den Interrupt 5 (BOUND Range exceeded) auslsen. Mit einer eigenen
     ISR knnte der Programmierer  sein Range-Checking auslagern und sich die
     sonst immer wiederkehrenden CMP/JB/JA-Gerste sparen.


   Exceptions:

     RM   Int 5 (Fail), Int 6 (Invalid), Int 13 (Wrap)
     VM   Int 5 (Fail), Int 6 (Invalid), Int 13 (Wrap), #PF[Code]
     PM   Int 5 (Fail), #UD, #SS[0], #GP[0], #PF[Code]


                                                           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-42| 10+3n -     -         0F BC
                                   7-43
   BSF r32, r|m32      NP    ?     6-42| 10+3n -     -         0F BC
                                   7-43


   Pseudocode:

     if SRC = 0 then
       ZF = 1
       DEST = ?
     else
       ZF = 0
       tmp = 0;
       while Bit[SRC:tmp] = 0 do
         tmp = tmp + 1
         DEST = tmp
       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-
     hlt 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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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    ?     6-103| 10+3n -     -         0F BD
                                   7-104
   BSR r32, r|m32      NP    ?     6-103| 10+3n -     -         0F BD
                                   7-104


   Pseudocode:

     if SRC = 0 then
       ZF = 1
       DEST = ?
     else
       ZF = 0
       tmp = OperandSize-1
       while Bit[SRC:tmp] = 0 do
         tmp = tmp - 1
         DEST = tmp
       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-
     hlt 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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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 /r


   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|4   3|3    3|6   -     -         0F BA /4 ib
   BT r|m32, imm8      NP    4|4   3|3    3|6   -     -         0F BA /4 ib


   Pseudocode:

     CF = DEST[Bit[SRC]]

   Beschreibung:

     Das SRCte Bit aus DEST wird ins CF kopiert.

     HINWEIS: SRC wird von der CPU  vor der Ausfhrung  mit MOD 32 verknpft,
              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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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 = DEST[Bit[SRC]]
     DEST[Bit[SRC]] = NOT DEST[Bit[SRC]]

   Beschreibung:

     Das SRCte Bit aus DEST wird ins CF kopiert. Danach wird das SRCte Bit in
     DEST geflipped, d.h. aus 1 wird 0 und umgekehrt.

     HINWEIS: SRC wird von der CPU  vor der Ausfhrung  mit MOD 32 verknpft,
              d.h. der Wertebereich wird 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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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 = DEST[Bit[SRC]]
     DEST[Bit[SRC]] = 0

   Beschreibung:

     Das SRCte Bit aus DEST wird ins CF kopiert. Danach wird das SRCte Bit in
     DEST gelscht.

     HINWEIS: SRC wird von der CPU  vor der Ausfhrung  mit MOD 32 verknpft,
              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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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 = DEST[Bit[SRC]]
     DEST[Bit[SRC]] = 1

   Beschreibung:

     Das SRCte Bit aus DEST wird ins CF kopiert. Danach wird das SRCte Bit in
     DEST gesetzt.

     HINWEIS: SRC wird von der CPU  vor der Ausfhrung  mit MOD 32 verknpft,
              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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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 Task Switch fhrt


   Befehl             Pipe   P5    486    386   286  86        Opcode

   
   CALL rel16          PV    ?     3      7+m   7     19        E8 cw

     Near, relativ

   
   CALL rel32          PV    ?     3      7+m   -     -         E8 cd

     Near, relativ

   
   CALL r|m16          NP    ?     5|5    7+m|  7|11  16|21+EA  FF /2
                                          10+m

     Near, indirekt Register|Memory

   
   CALL r|m32          NP    ?     5|5    7+m|  -     -         FF /2
                                          10+m

     Near, indirekt Register|Memory

   
   CALL ptr16:16       NP    ?     18     17+m  13    28        9A cd
                                   PM=    PM=   PM=
                                   20     34+m  26

     Far, Zeiger

   
   CALL ptr16:16       NP    ?     PM=    PM=   41    -         9A cd
                                   35     52+m

     Gate, gleiches Privileg

   
   CALL ptr16:16       NP    ?     PM=    PM=   82    -         9A cd
                                   69     86+m

     Gate, hheres Privileg, keine Parameter

   
   CALL ptr16:16       NP    ?     PM=    PM=   86+   -         9A cd
                                   77+    94+   4x
                                   4x     4x+m

     Gate, hheres Privileg, x Parameter

   
   CALL ptr16:16       NP    ?     PM=    TS    177|  -         9A cd
                                   37+TS        82

     Task                                        ber TSS | ber Task Gate

   
   CALL ptr16:32       NP    ?     18     17+m  -     -         9A cp
                                   PM=    PM=
                                   20     34+m

     Far, Zeiger

   
   CALL ptr16:32       NP    ?     PM=    PM=   -     -         9A cp
                                   35     52+m

     Gate, gleiches Privileg

   
   CALL ptr16:32       NP    ?     PM=    PM=   -     -         9A cp
                                   69     86+m

     Gate, hheres Privileg, keine Parameter

   
   CALL ptr16:32       NP    ?     PM=    PM=   -     -         9A cp
                                   77+    94+
                                   4x     4x+m

     Gate, hheres Privileg, x Parameter

   
   CALL ptr16:32       NP    ?     PM=    TS    -     -         9A cp
                                   37+TS

     Task

   
   CALL m16:16         NP    ?     17     22+m  16|   37+EA     FF /3
                                   PM=    PM=   29
                                   20     38+m

     Far, indirekt DWord Register|Memory

   
   CALL m16:16         NP    ?     PM=    PM=   44    -         FF /3
                                   35     56+m

     Gate, gleiches Privileg

   
   CALL m16:16         NP    ?     PM=    PM=   83    -         FF /3
                                   69     90+m

     Gate, hheres Privileg, keine Parameter

   
   CALL m16:16         NP    ?     PM=    PM=   90+   -         FF /3
                                   77+    98+   4x+m
                                   4x     4x+m

     Gate, hheres Privileg, x Parameter

   
   CALL m16:16         NP    ?     PM=    5+TS  180|  -         FF /3
                                   37+TS        185

     Task                                        ber TSS | ber Task Gate

   
   CALL m16:32         NP    ?     17     22+m  -     -         FF /3
                                   PM=    PM=   -
                                   20     38+m

     Far, indirekt DWord Register|Memory

   
   CALL m16:32         NP    ?     PM=    PM=   -     -         FF /3
                                   35     56+m

     Gate, gleiches Privileg

   
   CALL m16:32         NP    ?     PM=    PM=   -     -         FF /3
                                   69     90+m

     Gate, hheres Privileg, keine Parameter

   
   CALL m16:32         NP    ?     PM=    PM=   -     -         FF /3
                                   77+    98+
                                   4x     4x+m

     Gate, hheres Privileg, x Parameter

   
   CALL m16:32         NP    ?     PM=    5+TS  -     -         FF /3
                                   37+TS

     Task

   

    nur 286: plus 1 Taktzyklus pro Komponente des nchsten Befehls


   TS (CALL)                             Neuer Task

                    486 TSS  486 TSS VM  386 TSS  386 TSS VM  286 TSS
                                                             
   Alter Task                                                
                                                             
   486: 286 TSS       199      177       ---       ---       180
   
   386: 386 TSS       ---       ---        300      217      273
   
   386: 286 TSS       ---       ---        298      217      273
   

    bei CALLs via Task Gate sind weitere 9 Taktzyklen hinzuzuaddieren.


   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 CS_Sel = 0 then
             #GP[0]
           endif

           if not (CS_SelIdx_In_DesTblLimits) then
             #GP[new CS_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 = Zero_extended_new_offset

               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    = Zero_extended_new_offset

               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[0]
                   endif

                   if (EIP_out_of_CS_Limits) then
                     #GP[0]
                   endif

                   SS:[E]SP = TSS.SS:[E]SP
                   CS:EIP   = CG.CS:EIP

                 else

                   ; OperandSize = 16

                   if (ParamsPlus8Byte_don't_fit_on_stack)
                     #SS[0]
                   endif

                   if (IP_out_of_CS_Limits) then
                     #GP[0]
                   endif

                   SS:[E]SP = TSS.SS:[E]SP
                   CS:IP    = CG.CS:IP

                 endif

                 Load CS_Des
                 Load 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

                 Load 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 (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 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 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 ausgefhrt.  Nach Beendigung  der Prozedur (RET in
     der Prozedur) wird die Ausfhrung 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 mglich.  Voraussetzung ist das entsprech-
              ende Setzen  eines 16bit OperandSize Prefix  (s. Topic BAW0008)
              fr den CALL (es sind ja 16bit RETs zu erwarten). Da die CALLs
              innerhalb  der ersten 64 KByte  des 32bit-Segmentes liegen ms-
              sen, drfte offensichtlich sein (sonst kme es zum IP-Wrap).


   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code]
     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]


                                                           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:

     Lscht 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:

     Lscht das DF.  CLD beeinflusst das Verhalten  aller String-Instructions
     (LODS, STOS, MOVS, CMPS, SCAS, INS, OUTS).  Nach  einem CLD  werden  die
     betroffenen Indexregister ([E]SI und/oder [E]DI) bei String-Instructions
     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    6     5      3     3     2         FA


   Pseudocode:

     if PE = 1 then
       if CPL > IOPL then
         #GP[0]
       endif
     endif
     IF = 0

   Beschreibung:

     Lscht 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.


   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    ?     7      5     2     -         0F 06


   Pseudocode:

     if ((PE = 1) AND (VM = 0)) then
       if CPL <> 0 then
         #GP[0]
       endif
     endif
     CR0.TS = 0    (286: MSW.TS = 0)

   Beschreibung:

     Lscht 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 ausgefhrt
     werden.


   Exceptions:

     RM   -
     VM   -
     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      2     3     4         3C ib
   CMP AX, imm16       UV    ?     1      2     3     4         3D iw
   CMP EAX, imm32      UV    ?     1      2     -     -         3D id
   CMP r|m8, imm8      UV    ?     1|2    2|5   3|6   4|10+EA   80 /7 ib
   CMP r|m16, imm16    UV    ?     1|2    2|5   3|6   4|10+EA   81 /7 iw
   CMP r|m32, imm32    UV    ?     1|2    2|5   -     -         81 /7 id
   CMP r|m16, imm8     UV    ?     1|2    2|5   3|6   4|10+EA   83 /7 ib
   CMP r|m32, imm8     UV    ?     1|2    2|5   -     -         83 /7 ib
   CMP r|m8, r8        UV    ?     1|2    2|5   2|7   3|9+EA    38 /r
   CMP r|m16, r16      UV    ?     1|2    2|5   2|7   3|9+EA    39 /r
   CMP r|m32, r32      UV    ?     1|2    2|5   -     -         39 /r
   CMP r8, r|m8        UV    ?     1|2    2|6   2|6   3|9+EA    3A /r
   CMP r16, r|m16      UV    ?     1|2    2|6   2|6   3|9+EA    3B /r
   CMP r32, r|m32      UV    ?     1|2    2|6   -     -         3B /r


   Pseudocode:

     FLAGS for (DEST - SignExtend(SRC))

   Beschreibung:

     CMP subtrahiert CPU-intern SRC von DEST und setzt  (ohne ein Ergebnis in
     DEST abzulegen) die entsprechenden Flags.  Ist DEST ein Word  oder DWord
     und SRC ein imm8, wird SRC (ebenfalls nur CPU-intern) vorher automatisch
     vorzeichenbehaftet auf Word/DWord erweitert.

     CMP wird blicherweise im Zusammenhang mit Jcc-,  sowie (ab 386+) SETcc-
     Instructions angewandt, um abhngig von bestimmten Flagzustnden zu ver-
     zweigen.


   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
 Ŀ
  CMPS? - Compare String Operands                        * - - - * * * * *
 

   Befehl             Pipe   P5    486    386   286   86        Opcode

   CMPSB               NP    6     8      10    8     22        A6
   CMPSW               NP    7     8      10    8     22        A7
   CMPSD               NP    13?   8      10    -     -         A7
   CMPS m8, m8         NP    ?     8      10    8     22        A6
   CMPS m16, m16       NP    ?     8      10    8     22        A7
   CMPS m32, m32       NP    ?     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 DESTIdx]

       Word

         if DF = 0 then
           IncDec = 2
         else
           IncDec = -2
         endif
         FLAGS for [word ptr SRCIdx] - [word ptr DESTIdx]

       DWord

         if DF = 0 then
           IncDec = 4
         else
           IncDec = -4
         endif
         FLAGS for [dword ptr SRCIdx] - [dword ptr 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 DESTIdx zeigt  und setzt entsprechend die
     FLAGS.

     Abschlieend werden in Abhngigkeit 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? Instructions und dem [E]CX-Register knnen auch
     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 natrlich auch die FLAGS  entsprechend zu inter-
               pretieren.


   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
 Ŀ
  CMPXCHG - Compare and exchange                         * - - - * * * * *
 

   Befehl             Pipe   P5    486    386   286   86        Opcode

   CMPXCHG r|m8, r8    NP    6     6|7   -     -     -         0F A6 /r
   CMPXCHG r|m16, r16  NP    6     6|7   -     -     -         0F A7 /r
   CMPXCHG r|m32, r32  NP    6     6|7   -     -     -         0F A7 /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 = DEST, wird SRC in DEST, sonst DEST in Accu abgelegt.

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code], #AC[0] (nur CPL3)
     PM   #SS[0], #GP[0], #PF[Code], #AC[0] (nur CPL3)


                                                           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    -      -     -     -         ?


   Pseudocode:

      if EDX:EAX = [qword ptr DEST] then
        ZF = 1
        DEST = [qword ptr 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,  zeigen muss.  Gleiches gilt natrlich
     auch fr ECX:EBX (ist aus der Adressierungsart NICHT ersichtlich).

   Exceptions:

     RM   Int 6 (Invalid), Int 13 (Wrap)
     VM   Int 6 (Invalid), Int 13 (Wrap), #PF[Code], #AC[0] (nur CPL3)
     PM   #UD, #SS[0], #GP[0], #PF[Code], #AC[0] (nur CPL3)


                                                           O D I T S Z A P C
 Ŀ
  CPUID - CPU Identification                             - - - - - - - - -
 

   Befehl             Pipe   P5    486    386   286   86        Opcode

   CPUID               NP    9    9     -     -     -         0F A2
                             14   14

    bei PreLoad EAX = <> 0
    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 0f e d cb a 9 87 6 5 43 2 1 0
                                                                      
                                                                      
                 reserviert (alle 0)        Typ   Famly  Model  StpID 
           

            StpID = Stepping ID (CPU-spezifisch)
            Model = Model (CPU-spezifisch)
            Famly = Family (4 = i486, 5 = P5[4c/5c], 6 = PPro)
            Typ   = (CPU-spezifisch)

         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 0fedcba9876543210
                                                          
                                                A        F
                                          C     P  2  4  P
                                          MM    IXMMMTMIVU
                                          OC    O8CBSSBOMO
               reserviert (alle 0)        VA0000CBEPRCPBXC
           

            FPUOC = FPU on-chip
            VMX   = Virtual Mode Extension present
            IOB   = I/O Breakpoint supported
            4MBP  = 4 MByte Pages supported
            TSC   = Time Stamp Counter present
            MSR   = Model specified registers present (P5+)
            2MBP  = 2 MByte Pages supported (P6+)
            MCE   = Machine Check Exception supported
            X8B   = CMPXCHG8B-Instruction supported
            APIOC = Advanced PIC on-chip
            MCA   = Machine Check Architecture supported (P6+)
            CMOV  = CMOVcc-Instructions supported (P6+)

       2:
         EAX:EBX:ECX:EDX = CacheInfo (P6+)

           Ein Byte je Cache Configuration Descriptor (4*4 8bit):

             00H   NIL Descriptor (unbenutzt)

             01H   Code TLB, 4 KByte Pages (4-way, 64 Entries)
             02H   Code TLB, 4 MByte Pages (4-way,  4 Entries)

             03H   Data TLB, 4 KByte Pages (4-way, 64 Entries)
             04H   Data TLB, 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 486ern neueren Datums
     untersttzt (m.W. alle 486er seit 09/94).

   Exceptions:

     RM   -
     VM   -
     PM   -


                                                           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
     else
       AF = 0
     endif

     if (AL > 9fH) OR (CF = 1) then
       AL = AL + 60H
       CF = 1
     else
       CF = 0
     endif

   Beschreibung:

     Was AAA fr BCDs ist, ist DAA fr gepackte BCDs.  Auch beim Addieren von
     gepackten BCDs kann es zu einem Stellenberlauf kommen,  der zu falschen
     Ergebnissen fhren wrde. 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
     else
       AF = 0
     endif

     if (AL > 9fH) OR (CF = 1) then
       AL = AL - 60H
       CF = 1
     else
       CF = 0
     endif

   Beschreibung:

     DAS ist fr gepackte BCDs das gleiche, wie AAS fr ungepackte BCDs. Auch
     beim Subtrahieren  von gepackten BCDs  kann es zu ergebnisverflschenden
     Stellenunterlufen 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    2|6   2|7   3|15+EA   FE /1
   DEC r|m16           UV    ?     1|3    2|6   2|7   3|15+EA   FF /1
   DEC r|m32           UV    ?     1|3    2|6   -     -         ?
   DEC r16             UV    ?     1      2     2     3         48+rw
   DEC r32             UV    ?     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.  Grsse an alle  ehemaligen
              65xx-Progger... ;)


   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
 Ŀ
  DIV - Divide (unsigned)                                ? - - - ? ? ? ? ?
 

   Befehl             Pipe   P5     486    386    286   86        Opcode

   DIV r|m8            NP    17|17  16|16  14|17  -     -         F6 /6
   DIV r|m16           NP    25|25  24|24  22|25  -     -         F7 /6
   DIV r|m32           NP    44|44  40|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 fr Dividend,  Quotient (QReg) und Rest (RReg) sind (wie im
     Pseudocode ersichtlich) durch die Operandengre 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]
     PM   Int 0 (DivideError), #SS[0], #GP[0], #PF[Code]


                                                           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 bentigt 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 she 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 verndert,  sondern nur Zeit verbraucht (hier
     natrlich die Zeit zur bertragung an die FPU, keine Wartezeit).

     Bei normalem, fehlerfreiem Ablauf (ohne Exceptionauslsung) kann die CPU
     unmittelbar nach der Befehlsbermittlung an die FPU  weiterarbeiten. Sie
     ist nicht verpflichtet, auf das Ausfhrungsende des FPU-Befehles zu war-
     ten, kann es aber natrlich, falls gewnscht (siehe WAIT-Instruction).

     Detaillierte Erluterungen  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[0], #GP[0], #PF[Code], #MF[0]


   PRI                                                     O D I T S Z A P C
 Ŀ
  HLT - Halt                                             - - - - - - - - -
 

   Befehl             Pipe   P5    486    386   286   86        Opcode

   HLT                 --    ?     4      5     2     2         F4


   Pseudocode:

     Enter_Halt_State

   Beschreibung:

     HLT stoppt  die Ausfhrung 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 ausgelst
     wird.

     Der Instruction Pointer ([E]IP)  wird bei Ausfhrung 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 ausgefhrt
     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    ?     19|20  19   17|20  101-112|  F6 /6
                                                      107-118+
                                                      EA
   IDIV r|m16          NP    ?     27|28  27   25|28  165-184|  F7 /6
                                                      171-190+
                                                      EA
   IDIV r|m32          NP    ?     43|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 fr Dividend,  Quotient (QReg) und Rest (RReg) sind (wie im
     Pseudocode ersichtlich) durch die Operandengre 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]
     PM   Int 0 (DivideError), #SS[0], #GP[0], #PF[Code]


                                                           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   ?   13-18  9-14|  13|16  80-98|    F6 /5
                                            12-17         86-104+
                                                          EA
     AX = AL * r|m8

   
   IMUL r|m16               NP   ?   13-26  9-22|  21|24  128-154|  F7 /5
                                            12-25         134-160+
                                                          EA
     DX:AX = AX * r|m16

   
   IMUL r|m32               NP   ?   13-42  9-38|  -      -         F7 /5
                                            12-41

     EDX:EAX = EAX * r|m32

   
   IMUL r16, r|m16          NP   ?   13-26  9-22|  -      -         0F AF /r
                                            12-25

     r16 = r16 * r|m16

   
   IMUL r32, r|m32          NP   ?   13-42  9-38|  -      -         0F AF /r
                                            12-41

     r32 = r32 * r|m32

   
   IMUL r16, r|m16, imm8    NP   ?   13-26  9-14|  21|24  -         6B /r ib
                                            12-17

     r16 = r|m16 * SignExtend(imm8)

   
   IMUL r32, r|m32, imm8    NP   ?   13-42  9-14|  -      -         6B /r ib
                                            12-17

     r32 = r|m32 * SignExtend(imm8)

   
   IMUL r16, imm8           NP   ?   13-26  9-14|  21|24  -         6B /r ib
                                            12-17

     r16 = r16 * SignExtend(imm8)

   
   IMUL r32, imm8           NP   ?   13-42  9-14|  -      -         6B /r ib
                                            12-17

     r32 = r32 * SignExtend(imm8)

   
   IMUL r16, r|m16, imm16   NP   ?   13-26  9-22|  21|24  -         69 /r iw
                                            12-25

     r16 = r|m16 * imm16

   
   IMUL r32, r|m32, imm32   NP   ?   13-42  9-38|  -      -         69 /r id
                                            12-41

     r32 = r|m32 * imm32

   
   IMUL r16, imm16          NP   ?   13-26  9-22|  -      -         69 /r iw
                                            12-25

     r16 = r16 * imm16

   
   IMUL r32, imm32          NP   ?   13-42  9-38|  -      -         69 /r id
                                            12-41

     r32 = r32 * imm32

   


   Pseudocode:

     Result = Multiplicand * Multiplier

   Beschreibung:

     IMUL fhrt 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 gelscht:

       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 vollstndig in r16
       IMUL r16, r|m16, imm16   Ergebnis passt vollstndig in r16

       IMUL r32, r|m32          Ergebnis passt vollstndig in r32
       IMUL r32, r|m32, imm32   Ergebnis passt vollstndig in r32


   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
 Ŀ
  IN - Input from Port                                   - - - - - - - - -
 

   Befehl         Pipe   P5  486        386        286   86        Opcode

   IN AL, imm8     NP    ?   RM=14      RM=12      5     10        E4 ib
                             PM=8/28  PM=6/26
                             VM=27
   IN AX, imm8     NP    ?   RM=14      RM=12      5     10        E5 ib
                             PM=8/28  PM=6/26
                             VM=27
   IN EAX, imm8    NP    ?   RM=14      RM=12      -     -         E5 ib
                             PM=8/28  PM=6/26
                             VM=27
   IN AL, DX       NP    ?   RM=14      RM=13      5     8         EC
                             PM=8/28  PM=7/27
                             VM=27
   IN AX, DX       NP    ?   RM=14      RM=13      5     8         ED
                             PM=8/28  PM=7/27
                             VM=27
   IN EAX, DX      NP    ?   RM=14      RM=13      -     -         ED
                             PM=8/28  PM=7/27
                             VM=27

    wenn CPL  IOPL
    386: wenn CPL > IOPL oder VM = 1
     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 knnen direkt (imm8), hherliegende Ports nur
     via DX adressiert werden.

     Hinweis: Bei Verwendung der DX-Adressierung ist die tatschlich nutzbare
              Datenbreite  nicht automatisch  auch gleich 16bit.  Werden z.B.
              Adapterkarten adressiert,  die sich in einem ISA-Slot befinden,
              reduziert sich die tatschlich 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    2|6   2|7   3|15+EA   FE /0
   INC r|m16           UV    ?     1|3    2|6   2|7   3|15+EA   FF /0
   INC r|m32           UV    ?     1|3    2|6   -     -         FF /6
   INC r16             UV    ?     1      2     2     3         40+rw
   INC r32             UV    ?     1      2     -     -         40+rd


   Pseudocode:

     DEST = DEST + 1

   Beschreibung:

     INC erhht DEST um 1.

     Hinweis: hnlich wie bei DEC wird auch bei INC das CF NICHT beeinflusst.
              Nochmal Gre an alle  ehemaligen 65xx-Progger... ;)


   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
 Ŀ
  INS? - Input String Operands                           - - - - - - - - -
 

   Befehl        Pipe   P5  486         386        286   86        Opcode

   INSB           NP    ?   RM=17       RM=15      5     -         6C
                            PM=10/32  PM=9/29
                            VM=30
   INSW           NP    ?   RM=17       RM=15      5     -         6D
                            PM=10/32  PM=9/29
                            VM=30
   INSD           NP    ?   RM=17       RM=15      -     -         6D
                            PM=10/32  PM=9/29
                            VM=30
   INS r|m8, DX   NP    ?   RM=17       RM=15      5     -         6C
                            PM=10/32  PM=9/29
                            VM=30
   INS r|m16, DX  NP    ?   RM=17       RM=15      5     -         6D
                            PM=10/32  PM=9/29
                            VM=30
   INS r|m32, DX  NP    ?   RM=17       RM=15      -     -         6D
                            PM=10/32  PM=9/29
                            VM=30

    wenn CPL  IOPL
    386: wenn CPL > IOPL oder VM = 1
     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.

     Abschlieend wird in Abhngigkeit 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? Instructions und dem [E]CX-Register knnen auch
     Byte-, Word- oder DWord-Strings eingelesen werden.

     Hinweis: DX bedeutet normalerweise eine Datenbreite von 16bit.  Ob diese
              16bit tatschlich in voller Breite benutzt werden knnen, hngt
              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]
     PM   #SS[0], #GP[0], #PF[Code]


                                                           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 fhrt
   0 = Flags werden nur im Real Address Mode gelscht


   Befehl             Pipe   P5    486    386   286    86        Opcode

   
   INT imm8            NP    ?     30     37    23+m    51        CD ib

     Nummerierte Interrupts auer INT 3 (und INT 4 (INTO, s. dort))

   
   INT imm8            NP    ?     44     59    40+m    -         CD ib

     PM, gleiches Privileg

   
   INT imm8            NP    ?     77     99    78+m    -         CD ib

     PM, hheres Privileg

   
   INT imm8            NP    ?     86     119   -       -         CD ib

     aus VM auf PL0

   
   INT imm8            NP    ?     37+TS  TS    167+m   -         CD ib

     PM, ber Task Gate

   
   INT 3               NP    ?     26     33    23+m    52        CC

     Debugger Breakpoint

   
   INT 3               NP    ?     44     59    40+m    -         CC

     PM, gleiches Privileg

   
   INT 3               NP    ?     71     99    78+m    -         CC

     PM, hheres Privileg

   
   INT 3               NP    ?     82     119   -       -         CC

     aus VM auf PL0

   
   INT 3               NP    ?     37+TS  TS    167+m   -         CC

     PM, ber Task Gate

   

    nur 286: plus 1 Taktzyklus pro Komponente des nchsten Befehls


   TS (INT, INTO)                        Neuer Task

                    486 TSS  486 TSS VM  386 TSS  386 TSS VM  286 TSS
                                                             
   Alter Task                                                
                                                             
   486: 286 TSS       199       177        ---       ---       180
   
   386: 386 TSS       ---       ---        309       226       282
   
   386: 386 TSS VM    ---       ---        314       231       287
   
   386: 286 TSS       ---       ---        307       224       280
   


   Pseudocode:

     HINWEIS: Die folgende Beschreibung gilt fr Interrupts allgemein, sprich
              fr 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 ausgelster 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, Eintrgen.  Das erste Word  eines Eintrages enthlt den Offset-,
     das zweite Word den Segmentanteil  der ISR-Adresse (16:16),  zu der dann
     schlielich gesprungen wird.

     Der Descriptor Table (PM)  verwendet ein anderes Format. Es handelt sich
     zwar auch hier um ein Array mit 256 Eintrgen,  aber zum einen sind hier
     die Eintrge 8 Bytes lang  und zum anderen  enthalten die Eintrge 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 aufzufhren. Die wohl umfassenste Dokumen-
     tation hierzu ist imo Ralf Brown's Interrupt List (s. QLL0001).

   Exceptions:

     RM   Int 3 (Break), Int 13 (Wrap)
     VM   Int 3 (Break), Int 13 (Wrap), #GP[0] (INT n, IOPL < 3), #PF[Code]
     PM   #TS, #NP, #SS und #GP (wie im Pseudocode beschrieben), #PF[Code]


                                                           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 fhrt
   0 = Flags werden nur im Real Address Mode gelscht


   Befehl             Pipe   P5    486    386   286    86        Opcode

   
   INTO                NP    ?     28     35    24+m    53        CE

     INT 4, falls OF=1

   
   INTO                NP    ?     46     59    41+m    -         CE

     PM, gleiches Privileg

   
   INTO                NP    ?     73     99    79+m    -         CE

     PM, hheres Privileg

   
   INTO                NP    ?     84     119   -       -         CE

     aus VM auf PL0

   
   INTO                NP    ?     39+TS  TS    168+m   -         CE

     PM, ber Task Gate

   

   Taktzyklenangaben gelten fr OF=1 (OF=0: ?/3/3/3/4 (P5/486/386/286/86))

    nur 286: plus 1 Taktzyklus pro Komponente des nchsten Befehls


   Pseudocode und TS-Timings:

     siehe INT n

   Beschreibung:

     Ist whrend der Ausfhrung von INTO das OF gesetzt,  wird ein INT 4 aus-
     gelst.  INTO bietet dem Programmierer somit die Mglichkeit,  das Over-
     flow-Handling seines Programmes in eine ISR auszulagern. Dadurch kann er
     sich speicherintensive Gerste von JO/JNO/JMP Instructions sparen.

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #GP[0] (INT n, IOPL < 3), #PF[Code]
     PM   #TS, #NP, #SS und #GP (wie im Pseudocode beschrieben), #PF[Code]


   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 lscht alle internen CPU Cache Lines,  und sendet  einen speziellen
     Bus Cycle, der auch externe Cache Lines zum Lschen veranlasst.

     INVD ist eine privilegierte Instruction und kann nur bei CPL0 ausgefhrt
     werden.

     INVD ist implementationsspezifisch; seine Funktion kann variieren.

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap)
     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 Eintrge im TLB  als ungltig,  deren PagePart dem
     angegebenen mem-Wert entspricht.

     INVLPG ist eine privilegierte Instruction  und kann nur  bei CPL0 ausge-
     fhrt werden.

     INVLPG ist implementationsspezifisch; seine Funktion kann variieren.

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap)
     PM   #GP[0]


                                                           O D I T S Z A P C
 Ŀ
  IRET - Interrupt Return                                * * * * * * * * *
 

   Befehl             Pipe   P5  486    386    286     86        Opcode

   
   IRET                NP    ?   15     RM=22  RM=17+m  32        CF
                                        PM=38  PM=31+m

     Interrupt Return (16bit)

   
   IRET                NP    ?   36     82     55+m     -         CF

     PM, auf niedrigeres Privileg

   
   IRET                NP    ?   TS+32  TS     169+m    -         CF

     PM, zu anderem Task (NT=1)

   
   IRETD               NP    ?   15     RM=22  -        -         CF
                                        PM=38

     Interrupt Return (32bit)

   
   IRETD               NP    ?   36     82     -        -         CF

     PM, auf niedrigeres Privileg

   
   IRETD               NP    ?   TS+32  TS     -        -         CF

     PM, zu anderem Task (NT=1)

   
   IRETD               NP    ?   15     60     -        -         CF

     PM, in Virtual Mode

   

    nur 286: plus 1 Taktzyklus pro Komponente des nchsten Befehls


                                         Neuer Task

   TS (IRET/IRETD)  486 TSS  486 TSS VM  386 TSS  386 TSS VM  286 TSS
                                                             
   Alter Task                                                
                                                             
   486: 286 TSS       199       177        ---       ---       180
   
   386: 386 TSS       ---       ---        275       224       271
   
   386: 286 TSS       ---       ---        265       214       232
   


   Pseudocode:

     HINWEIS: Die folgende Beschreibung gilt fr Interrupts allgemein, sprich
              fr 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    = POP()
         CS     = POP()
         EFLAGS = POP()

       else

         ; IRET (16bit)

         IP    = POP()
         CS    = POP()
         FLAGS = POP()

       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 = POP()
           CS  = POP()
           NUL = POP()     ; throwaway old EFLAGS
           ES  = POP()     ; 2 words (1 + 1 throwaway)
           DS  = POP()     ; 2 words (1 + 1 throwaway)
           FS  = POP()     ; 2 words (1 + 1 throwaway)
           GS  = POP()     ; 2 words (1 + 1 throwaway)

           if CS.RPL > CPL then
             TmpESP = POP()
             TmpSS  = POP();
             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_[E]SP+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_[E]SP+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_[E]IP_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([E]SP, 12)
             else
               InternalLoad CS:IP from Stack
               InternalLoad CS with New_CS_Descriptor
               InternalLoad FLAGS with 3rd word from stack
               Inc([E]SP, 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 [[E]SP+8]
             else
               InternalLoad CS:IP from stack
               InternalLoad FLAGS with word at [[E]SP+4]
             endif

             InternalLoad SS:[E]SP from stack
             InternalSet  CPL = Return_CS_Sel.RPL
             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 Ausfhrung
     der ISR unterbrochene Programm wird nach IRET fortgesetzt.

     Im RM  werden dazu lediglich  die vor der ISR  auf dem Stack gesicherten
     Register [E]IP, CS und [E]FLAGS zurckgeladen.

     Im PM ist das ganze natrlich 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 Rckkehr  OHNE Taskswitch.  Das Programm,
         zu dem zurckgekehrt werden soll, darf nicht hher privilegiert sein
         als die ISR, sonst erntet man einen General Protection Fault... ;)

         Bei gleicher Privilegierung werden, hnlich wie im RM, nur [E]IP, CS
         und [E]FLAGS  vom Stack zurckgeladen.  Ist das Programm, zu dem zu-
         rckgekehrt werden soll,  niedriger privilegiert als die ISR, werden
         zustzlich auch das alte SS und [E]SP wieder hergestellt.

       NT = 1

         Hier erfolgt die  Rckkehr per Taskswitch.  Dabei wird der Task, der
         das IRET ausfhrte,  auf NOT BUSY gesetzt.  Wird dieser Task  spter
         erneut aufgerufen,  wird die  Programmausfhrung  automatisch hinter
         dem IRET wieder aufgenommen.

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #GP[0] (IOPL < 3), #PF[Code]
     PM   #TS, #NP, #SS und #GP (wie im Pseudocode beschrieben), #PF[Code]


                                                           O D I T S Z A P C
 Ŀ
  Jcc - Jump on Condition Code                           - - - - - - - - -
 

   Befehl             Pipe   P5    486    386     286  86       Opcode

   JA rel8             PV    ?     3,1   7+m,3  7,3  16,4    77 cb
   JA rel16|32         PV    ?     3,1   7+m,3  -     -        0F 87 cw|cd
   JAE rel8            PV    ?     3,1   7+m,3  7,3  16,4    73 cb
   JAE rel16|32        PV    ?     3,1   7+m,3  -     -        0F 83 cw|cd
   JB rel8             PV    ?     3,1   7+m,3  7,3  16,4    72 cb
   JB rel16|32         PV    ?     3,1   7+m,3  -     -        0F 82 cw|cd
   JBE rel8            PV    ?     3,1   7+m,3  7,3  16,4    76 cb
   JBE rel16|32        PV    ?     3,1   7+m,3  -     -        0F 86 cw|cd
   JC rel8             PV    ?     3,1   7+m,3  7,3  16,4    72 cb
   JC rel16|32         PV    ?     3,1   7+m,3  -     -        0F 82 cw|cd
   JCXZ rel8           PV    ?     3,1   9+m,5  8,4  18,6    E3 cb
   JE rel8             PV    ?     3,1   7+m,3  7,3  16,4    74 cb
   JE rel16|32         PV    ?     3,1   7+m,3  -     -        0F 84 cw|cd
   JECXZ rel8          PV    ?     3,1   9+m,5  -     -        E3 cb
   JG rel8             PV    ?     3,1   7+m,3  7,3  16,4    7F cb
   JG rel16|32         PV    ?     3,1   7+m,3  -     -        0F 8F cw|cd
   JGE rel8            PV    ?     3,1   7+m,3  7,3  16,4    7D cb
   JGE rel16|32        PV    ?     3,1   7+m,3  -     -        0F 8D cw|cd
   JL rel8             PV    ?     3,1   7+m,3  7,3  16,4    7C cb
   JL rel16|32         PV    ?     3,1   7+m,3  -     -        0F 8C cw|cd
   JLE rel8            PV    ?     3,1   7+m,3  7,3  16,4    7E cb
   JLE rel16|32        PV    ?     3,1   7+m,3  -     -        0F 8E cw|cd
   JNA rel8            PV    ?     3,1   7+m,3  7,3  16,4    76 cb
   JNA rel16|32        PV    ?     3,1   7+m,3  -     -        0F 86 cw|cd
   JNAE rel8           PV    ?     3,1   7+m,3  7,3  16,4    72 cb
   JNAE rel16|32       PV    ?     3,1   7+m,3  -     -        0F 82 cw|cd
   JNB rel8            PV    ?     3,1   7+m,3  7,3  16,4    73 cb
   JNB rel16|32        PV    ?     3,1   7+m,3  -     -        0F 83 cw|cd
   JNBE rel8           PV    ?     3,1   7+m,3  7,3  16,4    77 cb
   JNBE rel16|32       PV    ?     3,1   7+m,3  -     -        0F 87 cw|cd
   JNC rel8            PV    ?     3,1   7+m,3  7,3  16,4    73 cb
   JNC rel16|32        PV    ?     3,1   7+m,3  -     -        0F 83 cw|cd
   JNE rel8            PV    ?     3,1   7+m,3  7,3  16,4    75 cb
   JNE rel16|32        PV    ?     3,1   7+m,3  -     -        0F 85 cw|cd
   JNG rel8            PV    ?     3,1   7+m,3  7,3  16,4    7E cb
   JNG rel16|32        PV    ?     3,1   7+m,3  -     -        0F 8E cw|cd
   JNGE rel8           PV    ?     3,1   7+m,3  7,3  16,4    7C cb
   JNGE rel16|32       PV    ?     3,1   7+m,3  -     -        0F 8C cw|cd
   JNL rel8            PV    ?     3,1   7+m,3  7,3  16,4    7D cb
   JNL rel16|32        PV    ?     3,1   7+m,3  -     -        0F 8D cw|cd
   JNLE rel8           PV    ?     3,1   7+m,3  7,3  16,4    7F cb
   JNLE rel16|32       PV    ?     3,1   7+m,3  -     -        0F 8F cw|cd
   JNO rel8            PV    ?     3,1   7+m,3  7,3  16,4    71 cb
   JNO rel16|32        PV    ?     3,1   7+m,3  -     -        0F 81 cw|cd
   JNP rel8            PV    ?     3,1   7+m,3  7,3  16,4    7B cb
   JNP rel16|32        PV    ?     3,1   7+m,3  -     -        0F 8B cw|cd
   JNS rel8            PV    ?     3,1   7+m,3  7,3  16,4    79 cb
   JNS rel16|32        PV    ?     3,1   7+m,3  -     -        0F 89 cw|cd
   JNZ rel8            PV    ?     3,1   7+m,3  7,3  16,4    75 cb
   JNZ rel16|32        PV    ?     3,1   7+m,3  -     -        0F 85 cw|cd
   JO rel8             PV    ?     3,1   7+m,3  7,3  16,4    70 cb
   JO rel16|32         PV    ?     3,1   7+m,3  -     -        0F 80 cw|cd
   JP rel8             PV    ?     3,1   7+m,3  7,3  16,4    7A cb
   JP rel16|32         PV    ?     3,1   7+m,3  -     -        0F 8A cw|cd
   JPE rel8            PV    ?     3,1   7+m,3  7,3  16,4    7A cb
   JPE rel16|32        PV    ?     3,1   7+m,3  -     -        0F 8A cw|cd
   JPO rel8            PV    ?     3,1   7+m,3  7,3  16,4    7B cb
   JPO rel16|32        PV    ?     3,1   7+m,3  -     -        0F 8B cw|cd
   JS rel8             PV    ?     3,1   7+m,3  7,3  16,4    78 cb
   JS rel16|32         PV    ?     3,1   7+m,3  -     -        0F 88 cw|cd
   JZ rel8             PV    ?     3,1   7+m,3  7,3  16,4    74 cb
   JZ rel16|32         PV    ?     3,1   7+m,3  -     -        0F 84 cw|cd

    1. Wert: Condition Code Match (branch taken), 2. Wert: no branch
    nur 286: plus 1 Taktzyklus pro Komponente des nchsten Befehls


   Pseudocode:

     if (ConditionCode) then
       [E]IP = [E]IP + 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  prfen alle Jcc-Instructions,  ob bestimmte Bits
     oder Bitkombinationen  im FLAGS-Register momentan gesetzt und/oder gel-
     scht sind. JCXZ und JECXZ dagegen prfen nur, ob CX bzw. ECX = 0 sind.

     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 prfen, 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 gewnschte Bedingung ein, wird der [E]IP relativ zu seiner mo-
     mentanen Adresse um [rel8|rel16|rel32] Bytes nach vorne oder hinten ver-
     schoben und die Programmausfhrung dort fortgesetzt (Branch taken):

                 Bytes zurck .. Bytes vor

       rel8              -128 .. 127
       rel16          -32.768 .. 32.767
       rel32   -2.147.483.648 .. 2.147.483.647

     Es drfte einleuchten,  da mit relativen Sprngen  keine Segmentgrenzen
     berschritten werden sollten... ;)

     Stimmen die Bits  im FLAGS-Register  nicht mit dem gewnschten 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" fr Ver-
     gleiche  von vorzeichenbehafteten (signed) Werten,  die Begriffe "Above"
     und "Below" dagegen fr Byte-, Word- und DWord-Vergleiche (unsigned) ge-
     dacht sind.

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code]
     PM   #GP[0], #PF[Code]


                                                           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 fhrt


   Befehl             Pipe   P5  486    386      286   86        Opcode

   

   JMP rel8            PV    ?   3      7+m      7      15        EB cb

     Short

   

   JMP rel16           PV    ?   3      7+m      7      15        E9 cw

     Near

   

   JMP rel32           PV    ?   3      7+m      -      -         E9 cd

     Near

   

   JMP r|m16           NP    ?   5      7+m|     7+m|   11|       FF /4
                                        10+m     11+m   18+EA

     Near, indirekt

   

   JMP r|m32           NP    ?   5      7+m|     -      -         FF /4
                                        10+m

     Near, indirekt

   

   JMP ptr16:16        NP    ?   17     12+m     11     15     EA cd
                                 PM=19  PM=27+m  PM=23

     Far, 4-byte immediate (DWord)

   

   JMP ptr16:16        NP    ?   32     PM=45+m  38     -      EA cd

     Call Gate, gleiches Privileg

   

   JMP ptr16:16        NP    ?   42+TS  TS       175    -      EA cd

     Task State Segment

   

   JMP ptr16:16        NP    ?   43+TS  TS       180    -      EA cd

     Task Gate

   

   JMP m16:16          NP    ?   13     43+m     15     -      FF /5
                                 PM=18  PM=31+m  PM=26

     Far, indirekt

   

   JMP m16:16          NP    ?   31     PM=49+m  41     -      FF /5

     Call Gate, gleiches Privileg

   

   JMP m16:16          NP    ?   41+TS  5+TS     178    -      FF /5

     Task State Segment

   

   JMP m16:16          NP    ?   42+TS  5+TS     183    -      FF /5

     Task Gate

   

   JMP ptr16:32        NP    ?   13     12+m     -      -      EA cp
                                 PM=18  PM=27+m

     Far, 6-byte immediate (PWord)

   

   JMP ptr16:32        NP    ?   31     PM=45+m  -      -      EA cp

     Call Gate, gleiches Privileg

   

   JMP ptr16:32        NP    ?   42+TS  TS       -      -      EA cp

     Task State Segment

   

   JMP ptr16:32        NP    ?   43+TS  TS       -      -      EA cp

     Task Gate

   

   JMP m16:32          NP    ?   13     43+m     -      -      FF /5
                                 PM=18  PM=31+m

     Far, indirekt

   

   JMP m16:32          NP    ?   31     PM=49+m  -      -      FF /5

     Call Gate, gleiches Privileg

   

   JMP m16:32          NP    ?   41+TS  5+TS     -      -      FF /5

     Task State Segment

   

   JMP m16:32          NP    ?   42+TS  5+TS     -      -      FF /5

     Task Gate

   

    nur 286: plus 1 Taktzyklus pro Komponente des nchsten Befehls


                                         Neuer Task

   TS (JMP)         486 TSS  486 TSS VM  386 TSS  386 TSS VM  286 TSS
                                                             
   Alter Task                                                
                                                             
   486: 286 TSS       199      177       ---       ---       180
   
   386: 386 TSS       ---       ---        303      220      276
   
   386: 286 TSS       ---       ---        301      218      274
   

    bei JMPs via Task Gate sind weitere 9 Taktzyklen hinzuzuaddieren.


   Pseudocode:

     case AddrType of

       rel8, rel16, rel32

         ; Relativ

         [E]IP = [E]IP + 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 (New[E]IP_out_of_NewCS_Limits) then
                 #GP[0]
               endif

               CS = NewCS
               [E]IP = New[E]IP
               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 (New[E]IP_out_of_NewCS_Limits) then
                 #GP[0]
               endif

               CS = NewCS
               [E]IP = New[E]IP
               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.[E]IP_out_of_CG.CS_Limits) then
                 #GP[0]
               endif

               CS = [CG.CS]
               [E]IP = [CG.[E]IP]
               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 ([E]IP_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 ([E]IP_out_of_CS_Limits) then
                 #GP[0]
               endif

           else

             ; ungltiges 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 Programmausfhrung  an einer anderen gewnschten
     Adresse fortsetzen lassen.

     Da der Programmierer den Programflu durch JMPs bewut verndert, werden
     natrlich  weder Return-Adressen (wie bei CALLs) gesichert, noch irgend-
     welche Bits  zuvor im FLAGS-Register (wie bei Jccs) geprft. JMPs wirken
     im Prinzip wie direkte Wertzuweisungen an die Register CS und [E]IP, was
     aber auf normalem Weg (MOV IP, 1234H) bekanntlich nicht funktioniert ;)

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code]
     PM   #NP[CS_Sel|CG_Sel|TG_Sel|TSS_Sel], #GP[0|CS_Sel|CG_Sel], #PF[Code]


                                                           O D I T S Z A P C
 Ŀ
  LAHF - Load AH with FLAGS                              - - - - - - - - -
 

   Befehl             Pipe   P5    486    386   286   86        Opcode

   LAHF                NP    ?     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 fr mehrere aufeinan-
     derfolgende Vergleiche oder hnliches zwischenzuspeichern.

     Dies ist z.B. sinnvoll,  falls die Flags zwischen den Vergleichen beein-
     flut werden, bei weiteren Vergleichen aber der Urzustand bentigt wird.
     Zudem handelt es sich bei LAHF  auch um die schnellste Mglichkeit,  die
     Flags kurzzeitig zwischenzuspeichern (schneller als PUSHF/POP- oder MOV-
     Varianten).

     Hufig wird LAHF auch in Verbindung mit SAHF eingesetzt,  um die unteren
     Flags mit einem bestimmten Wert zu initialisieren,  der eine spezifische
     Ausgangsbasis  fr nachfolgenden Code garantiert,  oder einfach nur,  um
     auf einen Schlag  mehrere Flags gleichzeitig  setzen und/oder lschen zu
     knnen.


   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    ?     11    15|16  14|16  -       0F 02 /r
   LAR r32, r|m32      NP    ?     11    15|16  -      -       0F 02 /r


   Pseudocode:

     -

   Beschreibung:

     LAR wird blicherweise dazu benutzt um zu berprfen,  ob ein Zeiger auf
     ein gltiges Segment mit ausreichender Privilegstufe weist. Fr die Pr-
     fung bentigt man die AccessRights aus dem entsprechenden Descriptor.

     Mit LAR lsst sich im PM  das zweite DWord desjenigen Descriptors laden,
     dessen Selector man als SRC fr 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 gltigen 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, Mierfolg ent-
     sprechend durch ZF=0 an.

     Bei DEST = r32 wird das gesamte zweite DWord des Descriptors, bei DEST =
     r16 nur dessen LowWord geholt.  Unabhngig 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
     zustzlich  auch die Bits 24..31  (normal Bits 24..31  der Segment Base)
     ausgeblendet.

     Da die Bedeutung einiger Bits  im zweiten Descriptor-DWord natrlich vom
     Descriptor-Typ abhngt, nachfolgend kurz einmal die verschiedenen Typen:


                                      LAR r16, r|m16 Ĵ
     LAR r32, r|m32 Ĵ

     Descriptor-Typ: DATA (2. DWord)
     Ŀ
     1111111111111 1 1 1                        
     f e d c b a 9 876543 2 1 0fe dcba987 6 5 4 3 2 1 0
                                                       
                                                       
         von LAR                            von LAR    
       ausmaskiert     A         D        ausmaskiert  
                       V         P                     
         alle  0    GB0L? ? ? ?P L 10EWA    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 fr 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)
     Ŀ
     1111111111111 1 1 1                        
     f e d c b a 9 876543 2 1 0fe dcba987 6 5 4 3 2 1 0
                                                       
                                                       
         von LAR                            von LAR    
       ausmaskiert     A         D        ausmaskiert  
                       V         P                     
         alle  0    GD0L? ? ? ?P L 10CRA    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 fr den Programmierer)
       D   = Default (1: USE32, 0: USE16)
       G   = Granularity (1: 4KByte, 0: Byte)


     Descriptor-Typ: SPECIAL (2. DWord)
     Ŀ
     1111111111111 1 1 1                           
     f e d c b a 9 876543 2 1 0fe dcb a 9 87 6 5 4 3 2 1 0
                                                          
                                                          
         von LAR                               von LAR    
       ausmaskiert     A         D           ausmaskiert  
                       V         P                        
         alle  0    GX0L? ? ? ?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 Trap Gate
               0111  7H  286 Interrupt Gate
               1001  9H  386 TSS (available)
               1011  bH  386 TSS (busy)
               1100  cH  386 Call Gate
               1110  eH  386 Trap Gate
               1111  fH  386 Interrupt Gate

       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 fr den Programmierer)
       X   = reserviert
       G   = Granularity (1: 4KByte, 0: Byte)


   Exceptions:

     RM   Int 6 (Invalid)
     VM   Int 6 (Invalid)
     PM   #SS[0], #GP[0], #PF[Code]



                                                           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    ?    6       7      7      16+EA    C5 /r
                                  PM=12   PM=22  PM=21
   LDS r32, m16:32     NP    ?    6       7      -      -        C5 /r
                                  PM=12   PM=22

   Pseudocode:

     HINWEIS: Die folgende Beschreibung ist eine Zusammenfassung fr 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

         endif

         InternalLoad XSeg with Selector (incl. RPL Bits)
         InternalLoad XSeg with Descriptor

       endif

     endif


   Beschreibung:

     LDS ldt 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
     spter ein Zugriff auf ein solches Segment erfolgt... &)

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code]
     PM   #NP[DS_Sel], #GP[DS_Sel], #PF[Code]


                                                           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      2     3     2+EA      8D /r
   LEA r32, m          UV    ?     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, erhlt 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. einstellungsabhngig) in MOV-Instructions um,  um
              den RunTime-Code zu verkrzen (MOV ist in diesem Fall meist ein
              Byte krzer als LEA).

     LEA wurde frher  fast ausschlielich fr Initialisierungen vor der Aus-
     fhrung von z.B. XLAT- oder String-Instructions (LODS, STOS, MOVS, usw.)
     benutzt.

     Seit den 386ern "zweckentfremden" viele Programmierer LEA aber auch z.B.
     fr 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 knnen, auch wenn nur Register verwendet werden, ansonsten
     verabschiedet sich die CPU mit Invalid Opcode (Int 6, #UD)... &)


   Exceptions:

     RM   Int 6 (Invalid), Int 13 (Wrap)
     VM   Int 6 (Invalid), Int 13 (Wrap)
     PM   #UD[0]


                                                           O D I T S Z A P C
 Ŀ
  LEAVE - Release Stack Frame                            - - - - - - - - -
 

   Befehl             Pipe   P5    486    386   286   86        Opcode

   LEAVE               NP    ?     5      4     5     -         C9


   Pseudocode:

     if ((PE=1) AND (VM=0)) then

       ; reiner Protected Mode (kein VM)

       if [E]BP_out_of_Stack_Limits then
         #SS[0]
       endif

     endif

     if StackAddrSize = 16 then
       SP = BP
     else
       ESP = EBP
     endif

     if OperandSize = 16 then
       BP = POP(word)
     else
       EBP = POP(dword)
     endif

   Beschreibung:

     LEAVE wird dazu  benutzt,  ein zuvor eingerichtetes,  lokales Stackframe
     wieder freizugeben,  d.h. [E]BP und [E]SP wieder auf die Werte zurckzu-
     setzen, die sie vor der Einrichtung des Frames besaen.

     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 lsst.

     HINWEIS: Wie aus dem Pseudocode ersichtlich ist,  setzt LEAVE den Stack-
              pointer ([E]SP)  auf den  zum LEAVE-Zeitpunkt  aktuellen Inhalt
              des Basepointers ([E]BP) und POPed erst dann den ursprnglichen
              Basepointer vom Stack.

              Solange das lokale Stackframe  via ENTER eingerichtet wurde und
              [E]BP whrend der Routine  unangetastet  blieb,  sind bei LEAVE
              auch keine Probleme zu erwarten. Hufig ist es, speziell in der
              ASM-Programmierung, jedoch so,  da aus Geschwindigkeitsgrnden
              gar kein ENTER  zur Frame-Errichtung verwendet,  und/oder [E]BP
              aufgrund chronischen Registermangels ;)  bzw. wiederum aus Per-
              formancegrnden durch die Routine "mibraucht" wurde.

              In solchen Fllen  mu der Programmierer natrlich selbst dafr
              sorgen, da [E]BP wieder den "richtigen" Wert erhlt, bevor ein
              LEAVE ausgefhrt wird, ansonsten drfte der Guru gren... &)

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap)
     PM   #SS[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    ?    6       7      7      16+EA    C4 /r
                                  PM=12   PM=22  PM=21
   LES r32, m16:32     NP    ?    6       7      -      -        C4 /r
                                  PM=12   PM=22

   Pseudocode:

     siehe LDS

   Beschreibung:

     LES ldt 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
     spter ein Zugriff auf ein solches Segment erfolgt... &)

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code]
     PM   #NP[ES_Sel], #GP[ES_Sel], #PF[Code]


                                                           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    ?    6       7      -      -        0F B4 /r
                                  PM=12   PM=22
   LFS r32, m16:32     NP    ?    6       7      -      -        0F B4 /r
                                  PM=12   PM=22

   Pseudocode:

     siehe LDS

   Beschreibung:

     LFS ldt 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
     spter ein Zugriff auf ein solches Segment erfolgt... &)

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code]
     PM   #NP[FS_Sel], #GP[FS_Sel], #PF[Code]


   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    ?     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
       #UD[0]
     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-
     hlt 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 knnen.

     HINWEIS: Die Adressierungsart m16&32 wird oft unbewut als ptr16:32 mi-
              interpretiert. Bei m16&32 erwartet die CPU an Offset 0 das Word
              fr Limit (16bit),  und an Offset 2 das DWord fr Base (24 bzw.
              32bit (286 bzw. 386+)).

     LGDT ist eine privilegierte Instruction und kann nur bei CPL0 ausgefhrt
     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 "berhmten" Ausnahmen  fr den Einsatz von LGDT ist die Aufheb-
     ung der allseits bekannten 64 KByte-Segmentgrenzen, die normalerweise ja
     fr den Real Address Mode gelten. Bei entsprechender Vorbereitung ist es
     nmlich durchaus mglich, auch im RM mit bis zu 4 GByte groen Segmenten
     zu arbeiten... 8)

     Jeder, der sich schon einmal etwas intensiver mit der Programmierung von
     Demos, Intros oder Games beschftigt 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[0], #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    ?    6       7      -      -        0F B5 /r
                                  PM=12   PM=22
   LGS r32, m16:32     NP    ?    6       7      -      -        0F B5 /r
                                  PM=12   PM=22

   Pseudocode:

     siehe LDS

   Beschreibung:

     LGS ldt 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
     spter ein Zugriff auf ein solches Segment erfolgt... &)

   Exceptions:

     RM   Int 13 (Wrap)
     VM   Int 13 (Wrap), #PF[Code]
     PM   #NP[GS_Sel], #GP[GS_Sel], #PF[Code]


   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    ?     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
       #UD[0]
     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 enthlt der IDT  jedoch hchstens 256 Entries,  da ber den
     IDT nur ISRs, d.h. Programmcode fr Interrupt- und Exception-Handler an-
     gesprungen werden,  von denen es auf  IBM PCs (und Kompatiblen) bekannt-
     lich ja nur 256 Stck gibt... ;)

     HINWEIS: Die Adressierungsart m16&32 wird oft unbewut als ptr16:32 mi-
              interpretiert. Bei m16&32 erwartet die CPU an Offset 0 das Word
              fr Limit (16bit),  und an Offset 2 das DWord fr Base (24 bzw.
              32bit (286 bzw. 386+)).

     HINWEIS: In einem IDT sind nur  Task, Trap und Interrupt Descriptors zu-
              lssig.

     LIDT ist eine privilegierte Instruction und kann nur bei CPL0 ausgefhrt
     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[0], #SS[0], #GP[0], #PF[Code]



 === BAW0010 ================================================================

 MMX-Befehlssatz

 Thomas-Ivo Heinen 2:2449/830.3:

 [Hinweis:  Die Informationen ber MMX  sind (seitens Intel) nur sprlich und
 IMHO auch  widersprchlich  -  das eine  Mal existieren bestimmte Addressie-
 rungsarten, das andere Mal nicht. Darum bitte ich um Verstndnis fr Fehler]

 Ab dem P55C (neuste Pentia) hat INTEL  57 neue Befehle und 8 neue 64-Bit Re-
 gister eingefhrt - unter der Bezeichnung MMX (Multimedia Extensions). Damit
 knnen nun bis zu 8 (!) arithmetische Operationen gleichzeitig durchgefhrt
 werden. Bisher sollen folgende Prozessoren von Intel die MMX untersttzen:

    iP55C      (Pentium 3.Generation)
    iKlamath   (PentiumPro 2.Gen.)
    iDeschutes (PentiumPro 3.Gen)
    iMerced    (P7).
    AMD K6/NexGen6x86
    CyrixM2

 hnliche Kommandos existieren auch bei

    den SUN SPARCs (VPI)

 Etliche  Applets,  Spezifikationen, Beispiele und Dokumentationen  ber CPUs
 etc. von AMD, CYRIX und INTEL knnen bei 2:2449/830 requestet werden. Wer an
 ihnen  interessiert ist,  sollte  die Fileliste requesten  - eine Auflistung
 wre zu umfangreich- oder interessiert sich hier jemand fr Multiprocessing?

 MMX bringt das Konzept SIMD (Single Instruction Multiple Data) mit sich, das
 bis  zu acht Operationen  mit einem Befehl ermglicht.  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
     ...            ...
     111       MMX-Register7

 Im Folgenden werden die Befehle kurz erlutert.
 Da  keiner dieser  Befehle die Flags beeinflut,  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   Mer   Des   Kla   P55C             Opcode

   PADD mmx1,mmx2      UV     ?     1     2     3               24 ib


 BEFEHL

   In dieser Spalte  werden alle mglichen Adressierungsarten  des jeweiligen
   Befehles aufgefhrt. Dabei werden folgende Abkrzungen 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

   Parallele Ausfhrung durch DPA,die Pairing-Voraussetzungen sind noch nicht
   klar, werden aber in einer kommenden Version dieser FAQ ergnzt.

 Mer, Des, Kla, P55C

   In diesen Spalten  werden die Anzahl Taktzyklen angegeben,  die ein Befehl
   auf dem jeweiligen Prozessor zur Ausfhrung bentigt. Die Abkrzungen ste-
   hen fr:

        Mer  : "Merced",    P7, angeblich mit 500-1400 MHz (Ente ?)
        Des  : "Deschutes", P6 der dritten Generation mit mehr als 300 MHz
        Kla  : "Klamath",   P6 der zweiten Generation, mit MMX, ohne L2,
                             aber mit doppeltem L1 Cache, Takt grer 233 MHz
        P55C : "???"        Pentium der dritten Generation, mit MMX
                              ( 2.Generation: P54C, Takt: >= 75 Mhz       )
                              ( 1.Generation: P5,   Takt: 60/66 MHz       )

   Die zeitliche  Lnge der Befehle  liegt bei MMX  bis auf  wenige Ausnahmen
   schon beim  P55C bei  einem (!) Tick,  wegen der Ausnahmen  (z.B. PMADDWD)
   werden die Ticks im  Folgenden  jedoch  nichtsdestotrotz angegeben -  auch
   wenn's langweilig ist <g> ...

  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

   Ausfhrliche Beschreibung des Befehls und eventueller Besonderheiten.


 MGLICHE EXCEPTIONS

   Angaben zu mglichen Exceptions  bei der Anwendung des jeweiligen Befehles



 Ŀ
  EMMS - Empty MMX State                                                   
 

   Befehl                  PIPE   Mer   Des   Kla   P55C    Opcode

   EMMS                     ??     1     1     1     1      0F 77

   Beschreibung:
     Lscht das Statusbyte der MMX, nur erforderlich am Ende einer MMX-Unter-
     routine,  aber nur wirklich  notwendig, wenn im gleichen  Programm  auch
     Fliekomma-Arithmetik verwendet wird.

   Exceptions:
     #UD, falls kein MMX vorhanden
     #NM, falls CR0.TS=1

 Ŀ
  MOVD - Move Doubleword                                                   
 

   Befehl                  PIPE   Mer   Des   Kla   P55C    Opcode

   MOVD mmx32,reg32         ??     1     1     1     1      0F 6E 11mmmrrr
   MOVD reg32,mmx32         ??     1     1     1     1      0F 7E 11mmmrrr
   MOVD mmx32,mem32         ??     1     1     1     1      0F 6E oommmr/m
   MOVD mem32,mmx32         ??     1     1     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   Mer   Des   Kla   P55C    Opcode

   MOVQ mmx64,mmx64         ??     1     1     1     1      0F 6F 11mmmnnn
 ? MOVQ mmx64,mmx64         ??     1     1     1     1      0F 7F 11mmmnnn
   MOVQ mmx64,mem64         ??     1     1     1     1      0F 6F oommmr/m
   MOVQ mem64,mmx64         ??     1     1     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   Mer   Des   Kla   P55C    Opcode

   PACKSSDW mmx64,mmx64     ??     1     1     1     1      0F 6B 11mmmnnn
   PACKSSDW mmx64,mem64     ??     1     1     1     1      0F 6B oommmr/m

   Beschreibung:
     Konvertiert die jew. 2 Signed DWords des Source- und des Destinationre-
     gisters in 4 Signed Words im Destinationregister !?

   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   Mer   Des   Kla   P55C    Opcode

   PACKSSWB mmx64,mmx64     ??     1     1     1     1      0F 63 11mmmnnn
   PACKSSWB mmx64,mem64     ??     1     1     1     1      0F 63 oommmr/m

   Beschreibung:
     Konvertiert die jew. 4 Signed  Words des Source- und des Destinationre-
     gisters in 8 Signed Words im Destinationregister !?

   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   Mer   Des   Kla   P55C    Opcode

   PACKUSWB mmx8,mmx16      ??     1     1     1     1      0F 67 11mmmnnn
   PACKUSWB mmx8,mem16      ??     1     1     1     1      0F 67 oommmr/m

   Beschreibung:
     Konvertiert die jew. 4 Signed  Words des Source- und des Destinationre-
     gisters in 8 Unsigned Words im Destinationregister !?

   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   Mer   Des   Kla   P55C    Opcode

   PADDB mmx8,mmx8          ??     1     1     1     1      0F FC 11mmmnnn
   PADDB mmx8,mem8          ??     1     1     1     1      0F FC oommmr/m
   PADDD mmx16,mmx16        ??     1     1     1     1      0F FD 11mmmnnn
   PADDD mmx16,mem16        ??     1     1     1     1      0F FD oommmr/m
   PADDW mmx32,mmx32        ??     1     1     1     1      0F FE 11mmmnnn
   PADDW mmx32,mem32        ??     1     1     1     1      0F FE oommmr/m

   Beschreibung:
     Addiert den 2.Parameter zum 1. hinzu. Wenn ein Overflow erfolgt, "dreht"
     sich das Register wieder auf 0.

     Beispiel:
        MOV   EAX,$10204080
        MOV   EBX,$80808090
        MOVD  MMX0,EAX
        MOVD  MMX1,EBX
        PADDB MMX0,MMX1

        => MMX0 = $90A0C010

   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   Mer   Des   Kla   P55C    Opcode

   PADDSB mmx8,mmx8         ??     1     1     1     1      0F EC 11mmmnnn
   PADDSB mmx8,mem8         ??     1     1     1     1      0F EC oommmr/m
   PADDSW mmx16,mmx16       ??     1     1     1     1      0F ED 11mmmnnn
   PADDSW mmx16,mem16       ??     1     1     1     1      0F ED oommmr/m

   Beschreibung:
     Addiert den 2.Parameter zum 1. hinzu.  Wenn ein Overflow erfolgt, 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   Mer   Des   Kla   P55C    Opcode

   PADDUSB mmx8,mmx8        ??     1     1     1     1      0F DC 11mmmnnn
   PADDUSB mmx8,mem8        ??     1     1     1     1      0F DC oommmr/m
   PADDUSW mmx16,mmx16      ??     1     1     1     1      0F DD 11mmmnnn
   PADDUSW mmx16,mem16      ??     1     1     1     1      0F DD oommmr/m

   Beschreibung:
     Addiert den 2.Parameter zum 1. hinzu.  Wenn ein Overflow erfolgt, 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   Mer   Des   Kla   P55C    Opcode

   PAND mmx64,mmx64          ??     1     1     1     1      0F DB 11mmmnnn
   PAND mmx64,mem64          ??     1     1     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   Mer   Des   Kla   P55C    Opcode

   PANDN mmx64,mmx64        ??     1     1     1     1      0F DF 11mmmnnn
   PANDN mmx64,mem64        ??     1     1     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   Mer   Des   Kla   P55C    Opcode

   PCMPEQB mmx8,mmx8        ??     1     1     1     1      0F 74 11mmmnnn
   PCMPEQB mmx8,mem8        ??     1     1     1     1      0F 74 oommmr/m
   PCMPEQW mmx16,mmx16      ??     1     1     1     1      0F 75 11mmmnnn
   PCMPEQW mmx16,mem16      ??     1     1     1     1      0F 75 oommmr/m
   PCMPEQD mmx32,mmx16      ??     1     1     1     1      0F 76 11mmmnnn
   PCMPEQD mmx32,mem16      ??     1     1     1     1      0F 76 oommmr/m

   Beschreibung:
     Liefert im ersten Operanden $FF/$FFFF bzw. $FFFFFFFF falls die Bedingung
     erfllt ist, sonst $00/$0000/$00000000.

     Beispiel:

       MOV    EAX,$12342345;
       MOV    EBX,$12341234;
       MOVD   MMX0,EAX
       MOVD   MMX1,EBX
       PCMPEQ MMX0,MMX1

       => MMX0 = $FFFF0000

   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   Mer   Des   Kla   P55C    Opcode

   PCMPGTB mmx8,mmx8        ??     1     1     1     1      0F 64 11mmmnnn
   PCMPGTB mmx8,mem8        ??     1     1     1     1      0F 64 oommmr/m
   PCMPGTW mmx16,mmx16      ??     1     1     1     1      0F 65 11mmmnnn
   PCMPGTW mmx16,mem16      ??     1     1     1     1      0F 65 oommmr/m
   PCMPGTD mmx32,mmx16      ??     1     1     1     1      0F 66 11mmmnnn
   PCMPGTD mmx32,mem16      ??     1     1     1     1      0F 66 oommmr/m

   Beschreibung:
     Liefert im ersten Operanden $FF/$FFFF bzw. $FFFFFFFF falls die Bedingung
     erfllt ist, sonst $00/$0000/$00000000.

   Exceptions:
     #SS, #GP, #PF, #AC bei Mem-Operand
     #UD, falls kein MMX vorhanden
     #NM, falls CR0.TS=1

 Ŀ
  PMADD - Packed Multiply Add                                              
 

   Befehl                  PIPE   Mer   Des   Kla   P55C    Opcode

   PMADD  mmx64,mmx64       ??     ?     ?     ?     2      0F F5 11mmmnnn
   PMADD  mmx64,mem64       ??     ?     ?     ?     2      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   Mer   Des   Kla   P55C    Opcode

   PMULH  mmx64,mmx64       ??     1     1     1     1      0F E5 11mmmnnn
   PMULH  mmx64,mem64       ??     1     1     1     1      0F E5 oommmr/m

   Beschreibung:
     Die 4 Signed Words des Destinationregisters werden mit denen des Source-
     registers multipliziert. Die oberen 16Bit der jeweiligen Ergebnisse wer-
     den in das Destinationregister 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   Mer   Des   Kla   P55C    Opcode

   PMULL  mmx64,mmx64       ??     1     1     1     1      0F D5 11mmmnnn
   PMULL  mmx64,mem64       ??     1     1     1     1      0F D5 oommmr/m

   Beschreibung:
     Die 4 Signed Words des Destinationregisters werden mit denen des Source-
     registers multipliziert. Die oberen 16Bit der jeweiligen Ergebnisse wer-
     den in das Destinationregister geschrieben.

   Exceptions:
     #SS, #GP, #PF, #AC bei Mem-Operand
     #UD, falls kein MMX vorhanden
     #NM, falls CR0.TS=1

 Ŀ
  POR - Bitwise Or                                                         
 

   Befehl                  PIPE   Mer   Des   Kla   P55C    Opcode

   POR    mmx64,mmx64       ??     1     1     1     1      0F EB 11mmmnnn
   POR    mmx64,mem64       ??     1     1     1     1      0F EB oommmr/m

   Beschreibung:
     Bitweises Oder 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   Mer   Des   Kla   P55C    Opcode

   PSLLW  mmx16,mmx16       ??     1     1     1     1      0F F1 11mmmnnn
   PSLLW  mmx16,mem16       ??     1     1     1     1      0F F1 11mmmr/m
   PSLLW  mmx16,imm8        ??     1     1     1     1      0F F1 11110mmm ib
   PSLLD  mmx32,mmx32       ??     1     1     1     1      0F F2 11mmmnnn
   PSLLD  mmx32,mem32       ??     1     1     1     1      0F F2 11mmmr/m
   PSLLD  mmx32,imm8        ??     1     1     1     1      0F F2 11110mmm ib
   PSLLQ  mmx64,mmx64       ??     1     1     1     1      0F F3 11mmmnnn
   PSLLQ  mmx64,mem64       ??     1     1     1     1      0F F3 11mmmr/m
   PSLLQ  mmx64,imm8        ??     1     1     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
     eigenstndige 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   Mer   Des   Kla   P55C    Opcode

   PSRAW  mmx16,mmx16       ??     1     1     1     1      0F E1 11mmmnnn
   PSRAW  mmx16,mem16       ??     1     1     1     1      0F E1 11mmmr/m
   PSRAW  mmx16,imm8        ??     1     1     1     1      0F E1 11100mmm ib
   PSRAD  mmx32,mmx32       ??     1     1     1     1      0F E2 11mmmnnn
   PSRAD  mmx32,mem32       ??     1     1     1     1      0F E2 11mmmr/m
   PSRAD  mmx32,imm8        ??     1     1     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
     eigenstndige 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   Mer   Des   Kla   P55C    Opcode

   PSRLW  mmx16,mmx16       ??     1     1     1     1      0F D1 11mmmnnn
   PSRLW  mmx16,mem16       ??     1     1     1     1      0F D1 11mmmr/m
   PSRLW  mmx16,imm8        ??     1     1     1     1      0F D1 11010mmm ib
   PSRLD  mmx32,mmx32       ??     1     1     1     1      0F D2 11mmmnnn
   PSRLD  mmx32,mem32       ??     1     1     1     1      0F D2 11mmmr/m
   PSRLD  mmx32,imm8        ??     1     1     1     1      0F D2 11010mmm ib
   PSRLQ  mmx64,mmx64       ??     1     1     1     1      0F D3 11mmmnnn
   PSRLQ  mmx64,mem64       ??     1     1     1     1      0F D3 11mmmr/m
   PSRLQ  mmx64,imm8        ??     1     1     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
     eigenstndige 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   Mer   Des   Kla   P55C    Opcode

   PSUBB  mmx8,mmx8         ??     1     1     1     1      0F FB 11mmmnnn
   PSUBB  mmx8,mem8         ??     1     1     1     1      0F FB oommmr/m
   PSUBW  mmx16,mmx16       ??     1     1     1     1      0F FC 11mmmnnn
   PSUBW  mmx16,mem16       ??     1     1     1     1      0F FC oommmr/m
   PSUBD  mmx32,mmx32       ??     1     1     1     1      0F FD 11mmmnnn
   PSUBD  mmx32,mem32       ??     1     1     1     1      0F FD oommmr/m

   Beschreibung:
     Subtrahiert den 2.Parameter vom 1. . Wenn ein Underflow erfolgt, "dreht"
     sich das Register wieder auf $FF.

   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   Mer   Des   Kla   P55C    Opcode

   PSUBSB mmx8,mmx8         ??     1     1     1     1      0F E8 11mmmnnn
   PSUBSB mmx8,mem8         ??     1     1     1     1      0F E8 oommmr/m
   PSUBSW mmx16,mmx16       ??     1     1     1     1      0F E9 11mmmnnn
   PSUBSW mmx16,mem16       ??     1     1     1     1      0F E9 oommmr/m

   Beschreibung:
     Subtrahiert den 2.Parameter vom 1. . Wenn ein Underflow erfolgt,  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

 Ŀ
  PSUBUS - Subtract Unsigned with Saturation                               
 

   Befehl                  PIPE   Mer   Des   Kla   P55C    Opcode

   PSUBUSB mmx8,mmx8        ??     1     1     1     1      0F D8 11mmmnnn
   PSUBUSB mmx8,mem8        ??     1     1     1     1      0F D8 oommmr/m
   PSUBUSW mmx16,mmx16      ??     1     1     1     1      0F D9 11mmmnnn
   PSUBUSW mmx16,mem16      ??     1     1     1     1      0F D9 oommmr/m

   Beschreibung:
     Subtrahiert den 2.Parameter vom 1. . Wenn ein Underflow erfolgt,  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

 Ŀ
  PUNPCKH - Unpack High Data to Next Larger                                
 

   Befehl                  PIPE   Mer   Des   Kla   P55C    Opcode

   PUNPCKHB mmx8,mmx8       ??     1     1     1     1      0F 68 11mmmnnn
   PUNPCKHB mmx8,mem8       ??     1     1     1     1      0F 68 oommmr/m
   PUNPCKHW mmx16,mmx16     ??     1     1     1     1      0F 69 11mmmnnn
   PUNPCKHW mmx16,mem16     ??     1     1     1     1      0F 69 oommmr/m
   PUNPCKHD mmx32,mmx32     ??     1     1     1     1      0F 6A 11mmmnnn
   PUNPCKHD mmx32,mem32     ??     1     1     1     1      0F 6A oommmr/m

   Beschreibung:
     Originalbeschreibung: "Interleaves the bits of the operands", hier fr
     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   Mer   Des   Kla   P55C    Opcode

   PUNPCKLB mmx8,mmx8       ??     1     1     1     1      0F 60 11mmmnnn
   PUNPCKLB mmx8,mem8       ??     1     1     1     1      0F 60 oommmr/m
   PUNPCKLW mmx16,mmx16     ??     1     1     1     1      0F 61 11mmmnnn
   PUNPCKLW mmx16,mem16     ??     1     1     1     1      0F 61 oommmr/m
   PUNPCKLD mmx32,mmx32     ??     1     1     1     1      0F 62 11mmmnnn
   PUNPCKLD mmx32,mem32     ??     1     1     1     1      0F 62 oommmr/m

   Beschreibung:
     Originalbeschreibung: "Interleaves the bits of the operands", hier fr
     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   Mer   Des   Kla   P55C    Opcode

   PXOR   mmx64,mmx64      ??     1     1     1     1      0F EF 11mmmnnn
   PXOR   mmx64,mem64      ??     1     1     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
 

  INTEL ARCHITECTURE MMX TECHNOLOGY - FREQUENTLY ASKED QUESTIONS
   Internet: http://www.x86.org
   FRequest: MMXFAQ.RAR@2:2449/830    (   8 kB)

  INTEL MMX TECHNOLOGY OVERVIEW - MARCH 1996
   Internet: http://www.intel.com\pc-supp\multimed\mmx
   OrderNo : 243081-002

  INTEL ARCHITECTURE MMX TECHNOLOGY - PROGRAMMER'S REFERENCE MANUAL
   Internet: http://www.intel.com\pc-supp\multimed\mmx
   FRequest: MMXTECH.RAR@2:2449/830   ( 384 kB)
   OrderNo : 243007-001

  INTEL ARCHITECTURE MMX TECHNOLOGY - DEVELOPERS'S REFERENCE MANUAL
   Internet: http://www.intel.com\pc-supp\multimed\mmx
   FRequest: MMXPROG.RAR@2:2449/830   (1996 kB)
   OrderNo : 243006-001

  MULTIMEDIA INSTRUCTION SET FOR A 6TH GENERATION X86 PROCESSOR
   Internet: http://www.cyrix.com/process/prodinfo/6x86
   FRequest: HC-MMX4.RAR@2:2449/830   (  95 kB)

  AMD K6 MMX-INSTRUCTION SET
   Internet: http://www.amd.com/html/products/pcd/techdocs/techdocs.html
   FRequest: 20726A.RAR@2:2449/830    (1081 kB)

  C'T 06/96 - REGISSEUR UND KAMERAMANN, INTELS MMX- und AGP-Programm
   Seiten  : 206 - 208



 === BAW0011 ================================================================

 FPU-Befehlssatz

 Juergen Thelen 2:2450/645.5:



 === BAW0012 ================================================================

 Real Mode

 Juergen Thelen 2:2450/645.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 knnen.

 Der Modus wurde mit der ersten CPU der PC-ra - in unserem Raum war das m.W.
 der 8086 der Firma Intel - eingefhrt und existiert aus Grnden der Abwrts-
 kompatibilitt auf allen Nachfolge-CPUs bis hin zum heute aktuellen PPro.

 Neben diversen anderen,  hier aber eher  nebenschlichen Dingen,  wird durch
 den aktiven Prozessormodus auch bestimmt,  wie die CPU Angaben von Speicher-
 adressen interpretiert  bzw. in welchem Format diese vorliegen mssen, damit
 die CPU aus der Angabe eine tatschliche 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 verfgte.

 Es mute also ein Format her, welches ermglicht, den gesamten 20bit breiten
 Adressraum  anzusprechen,  gleichzeitig aber auch  fr einen  16bit Datenbus
 noch transportierbar ist (und das mglichst schnell).

 Naturgem wollten die Entwickler die 16bit-Fhigkeiten 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 groe
 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-Segment inner-
 halb des 20bit-Adressraumes (1 MByte) festgelegt werden? Da man 20bit nicht
 in einem Rutsch bertragen konnte, ist klar (Beispielwert 10000H):


    ?  Word (16bit) Ĵ
   Ŀ
   1111                
   3210fedcba9876543210
   
    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 htte auch funktioniert.  Eine solche Vorgehensweise birgt aber auch
 einen  gewaltigen Nachteil,  der auf den ersten Blick  nicht direkt ins Auge
 fllt.  Wren die Entwickler des 8086 tatschlich so vorgegangen,  htte das
 zur Folge gehabt, da die gesamten 1 MByte ansprechbaren Adressraums automa-
 tisch in 16 feste Segmente unterteilt worden wren:


           Ŀ  Ŀ  1.024 KByte
   16. Seg  f0000..fffffH      
           Ĵ    
           :                :    :
           :                :    :
           Ĵ     Adressraum 20bit (1 MByte = 1024 KByte)
            10000..1ffffH      
           Ĵ    
    1. Seg  00000..0ffffH      
               0 KByte


 Das htte nicht nur  die Bewegungsfreiheit des Programmierers eingeschrnkt,
 sondern wre auch  weder konomisch gewesen (kleinere Programme htten immer
 volle 64 KByte belegt,  selbst wenn sie z.B. nur 10 KByte gebraucht htten),
 noch htte man in diesem Fall Segmente sich berlappen lassen knnen.

 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 knnen.  Btw:  Hufig liest man statt 0MOD16 auch Paragraph, gemeint ist
 aber das gleiche. Nachfolgend ein Beispiel fr 64 KByte-Segmente, die an ei-
 ner willkrlich gewhlten 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 unumgnglich 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 fr 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
 drfte das segmentierte Speichermodell begriffen haben... B)

 Kommen wir abschlieend  zur einer Besonderheit,  die durch  den Einsatz des
 segmentierten Speichermodells berhaupt erst entsteht:  Welches ist wohl die
 grtmgliche segmentierte Angabe?

 Richtig: ffff:ffffH...

 Das entspricht doch wohl auch der physikalisch grtmglichen Adresse in ei-
 nem 20bit-Adressraum, nmlich 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
 hchstmgliche 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 knnten 21bit-Adressen doch problemlos verarbei-
 tet werden, oder?

 Wrden Sie auch ;),  wenn es da nicht einen  mit dem 80286  eingefhrten Me-
 chanismus gbe,  der sich A20-Gate nennt.  Bei diesem Mechanismus handelt es
 um ein simples Freischaltungsverfahren das die 21ste (A0..A20) und somit al-
 le darberliegenden Adressleitungen beeinflusst. Bei der Initialisierung des
 RM wird A20 einfach gesperrt, woraufhin sich auch alle greren CPUs genauso
 verhalten, wie der 8086, halt so, als htten sie nur 20 Adressleitungen.

 Das Freischalten von A20 ist brigens  einer der Schlssel,  die Pseudo-Pro-
 zessormodi wie RM/Flat4G (s. BAW0015) berhaupt erst ermglichen... B)

 Als weitaus  bekannteres Beispiel  fr 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
 fr Programme nutzbar zu machen. Unter DOS wird dieser zustzliche Speicher-
 raum dann High Memory Area (HMA) genannt. DOS verwendet die HMA  z.B. um auf
 Anweisung einen Groteil seines Kernels  dorthin zu verlagern, was natrlich
 positiv auf dem Konto freien konventionellen RAMs (unter 640 KByte) zu Buche
 schlgt.



 === BAW0013 ================================================================

 Protected Mode

 === BAW0014 ================================================================

 Virtual 86 (V86) Mode

 === BAW0015 ================================================================

 RM/Flat4G Mode

 Juergen Thelen 2:2450/645.5:

 Vorab einige Informationen fr solche, die mit dem Begriff RM/Flat4G (andere
 Programmierer nennen den Modus auch Big, Flat, Huge Real Mode, oder hnlich)
 absolut nichts anfangen knnen:

 Es handelt sich hier um die Bezeichnung fr einen Pseudo-Betriebsmodus,  der
 es  dem Programmierer ermglicht,  auch im Real Address Mode (RM)  unter DOS
 das RAM linear adressieren zu knnen (normalerweise ist dies aufgrund des im
 RM blichen segmentierten Speichermodells (64 KByte) nicht mglich).

 Hh? 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 fr 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 Vorgnge wie Task Switches, Segmentregister-Loads, usw.

 Der Hauptvorteil des PM  im Zusammenhang mit RM/Flat4G ist dessen Fhigkeit,
 Speicher  linear adressieren zu knnen.  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 fr segmentierte Speichermodelle nahezu unerreichbar ist.

 Um unter DOS im RM auf RAM oberhalb der 640 KByte-Grenze zugreifen, existie-
 ren  zwar auch einige Mglichkeiten (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 fr die CPU immer nur ein 64 KByte-Fenster des ge-
 samten oberen RAMs "sichtbar" ist.  Der Zugriff wird durch das stndige Ein-
 und Ausblenden beim "Verschieben" des Fensters qulend 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 fr den Eigenbedarf sperren kann  (schtzt vor Konflikten
 mit anderen Programmen),  ist (gemessen an linearer Adressierung)  aber auch
 nicht gerade das Gelbe vom Ei.

 Doch zurck zum Topic... ;)

 Ok, wie realisieren wir nun RM/Flat4G?

  Als erstes  bentigen 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  fr einen 80286 wre) adressieren kann.  Letzteres
   bringt natrlich 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  (prft
   in einem Rutsch auf 386+ im RM).

  Desweiteren mssen wir aus mehreren Grnden,  die im weiteren Verlauf noch
   offensichtlich werden drften,  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-
   rck in den RM zu "zwingen",  aber erstens  ist das eine aufwendige Sache,
   die  sehr tiefgreifende  Kenntnisse erfordert  (will man alle erdenklichen
   Situationen bercksichtigen),  und zweitens  zieht man sich mit einer sol-
   chen Aktion i.d.R. auch schnell den Zorn des Anwenders zu... >-)

   Zumeist lsst 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 bentigt werden, oder was auch immer...

   Fr mich persnlich 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  grundstzlich
   zurck in den RM bekommt (sorry,  nur ein paar Mglichkeiten  und die auch
   nur kurz angerissen, wird sonst endlos):

     PM -> RM

       Falls Paging aktiviert ist, zuerst folgendes: Sprung zu vom Paging un-
       berhrtem Code (linear=physikalisch), Paging-Bit (CR0.PG) lschen, und
       schlielich PageTable lschen (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 lschen (CR0.PE),  PQ-Flush,
       IDTR mit Adresse  der RM-Interruptvektortabelle laden,  IRQs freigeben
       (STI), Segmentregister-Init fr weiteren gewnschten RM-Betrieb.

     VM -> RM

       TaskSwitch via TaskGate,  oder IRET bei EFLAGS.NT=1  (EFLAGS des neuen
       Tasks mu natrlich VM=0 haben).

       Eine andere Mglichkeit  wre z.B. das Auslsen  eines Interrupts oder
       einer Exception ber Interrupt oder Trap Gate  zu einem Non-conforming
       CPL0-Codesegment... &)

   Doch zurck 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)
   eingefhrt.  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. Fr 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 fr 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-
   littsgrnden natrlich nur auf die unteren 16 Bits).  Fr 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
   ausgefhrt werden kann,  wenn das Programm keinen CPL0 besitzt (was in un-
   serem Fall ja durchaus sein knnte).  Ein MOV r32, CR0 wrde im PM bei ei-
   nem CPL > 0 zu einer allgemeinen Schutzverletzung (#GP[0]) fhren... &)

  Der nchste Punkt, um den wir uns kmmern mssen, ist ein Kompatibilitts-
   Handicap,  da alle CPUs ab 80286 mit sich herumschleppen,  um den RM 100%
   abwrtskompatibel zum 8086 betreiben zu knnen:

   Der RM (s. dazu auch BAW0012) wurde fr 8086 CPUs implementiert. Eine 8086
   CPU verfgt  lediglich  ber 20 Adressleitungen,  kann also maximal bis zu
   1 MByte (2^20 - 1) RAM adressieren.  Adressierungen  ber diesen Punkt hi-
   naus schlgt die CPU wieder auf den Anfang um (sogenannter WrapAround).

   CPUs ab 80286 besitzen jedoch bekanntlich mehr als 20 Adressleitungen, al-
   so mu ein Mechanismus  dafr sorgen,  da im RM keine Zugriffe ber diese
   ursprnglich maximal 20 Adressleitungen hinaus erfolgen,  ansonsten ist es
   Essig mit der Abwrtskompatibilitt...

   Die Lsung fr dieses Problem  ist das sogenannte A20-Gate.  CPUs ab 80286
   sperren  whrend der CPU-Initialisierung fr den RM  ber dieses Gate ein-
   fach die 21ste Adressleitung (A20) und somit auch alle hherliegenden.

   Fr uns bzw. RM/Flat4G  bedeutet dies natrlich,  da wir A20 wieder frei-
   schalten mssen, um die CPU zumindest schon einmal aus dem grundstzlichen
   1 MByte-Dilemma des RM  zu befreien.  Damit ist zwar  die letzte Barriere,
   die berwindung der 64 KByte-Segmentgrenzen, noch nicht durchbrochen, doch
   dazu spter 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 mssen,  muss der Programmierer nur noch ber INT 2f.4300H
   prfen, 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 drfte heutzutage zwar bei den meisten Anwendern defaultmig in
   Betrieb  sein (alleine schon,  um das DOS-Kernel  in die HMA  auslagern zu
   knnen), aber sicher ist auf unserer Welt bekanntlich gar nix... ;)

   Fr den Fall, da kein XMS-Treiber aktiv ist,  haben wir nun zwei Mglich-
   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
   mgliche  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 natrlich etwas vollkommen
   anderes bewirken.

   Wer generell mitrauisch ist ;), oder sich im Fall der Verwendung von exo-
   tischen Keyboard Controllern  zumindest eine relative Gewissheit verschaf-
   fen mchte, da die direkte A20-Freischaltung von Erfolg gekrnt war, kann
   nachprfen,  ob sich durch die Umschaltung das WrapAround-Verhalten vern-
   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.

   Beispiellsungen zur direkten A20-Steuerung  bzw.  WrapAround-Prfung sind
   brigens in LSG0006 (RM/Flat4G und RAW) enthalten.

  Zuletzt mssen wir  nun noch dafr  sorgen,  da wir das lstige und lahme
   segmentierte Speichermodell, genauer die 64KByte-Segmentgrenzen, loswerden
   und stattdessen endlich linear adressieren knnen... 8)

   Das Problem hierbei ist: Die lineare Adressierung funktioniert bekanntlich
   nur im Protected Mode. Wie bringen wir nun die CPU dazu, diese nur fr den
   ursprnglichen RM erforderlichen 64 KByte-Limits zu vergessen?

   Nun, das ganze Geheimnis basiert,  wie so oft bei Intel CPUs ;), auf einem
   fr lange Zeit undokumentierten Feature.  Irgendwann (wann genau, wei ich
   nicht mehr.  Ich persnlich erfuhr davon jedenfalls so etwa um den Jahres-
   wechsel 86/87) sickerte inoffiziell ;) durch, da die Intel CPUs bei der
   Rckkehr vom PM  in den RM  ihre internen Bereiche,  u.a. auch die fr das
   Descriptor-Caching, nicht leeren.

   Um zu verstehen, was das bedeutet, bentigt 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,  fr "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 natrlich alle nachfolgenden Zugriffe (Base, Limit,
   AccessRights, usw.) auf zugehrige Segmente  bzw.  deren Deskriptoren auch
   wesentlich schneller durchfhren.

   Fr 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 Rckkehr in den RM nicht lscht,  ist unser Schlssel dazu,
   den 64 KByte-Riegel berhaupt knacken zu knnen... B)

   Alles was wir im Prinzip tun mssen 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 abschlieend wieder zurck in den RM zu schalten... ;)

   Wer sich mit dem PM noch nicht so recht auskennt, dem sei in diesem Zusam-
   menhang die Lektre der Instructions LAR und LGDT im Topic BAW0009 empfoh-
   len. Desweiteren drften auch LSG0005 und LSG0006 evtl. hilfreich sein...

  Oops, einen wichtigen Hinweis htte 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 hchstwahrscheinlich be-
   reits  einige Instructions  vordekodiert hat  und fr den Execution Stream
   bereithlt.  Wrde in diesem Zustand der Prozessor umgeschaltet,  erhielte
   man allenfalls einen kostenlosen Crash-Test... 8)

   Auslsen  lsst sich  ein PQ-Flush  auf verschiedene Weise, am einfachsten
   jedoch ber eine JMP-Instruction.

 So, das wars frs Erste zum RM/Flat4G.  Viel Spa beim Experementieren... ;)



 === BAW0016 ================================================================

 Interrupts

 === BAW0017 ================================================================

 Exceptions

 Juergen Thelen 2:2450/645.5:

 Anmerkung: Ich verwende in diesem Topic immer die Bezeichnung ISR (Interrupt
            Service Routine)  fr den Programmteil,  der bei  Auslsung einer
            Exception  die Kontrolle erhlt.  Andere Autoren  mgen in diesem
            Zusammenhang vielleicht eher  die Begriffe Exception o. Interrupt
            Handler benutzen. Es ist aber das gleiche gemeint... ;)

 Exceptions werden immer dann ausgelst,  wenn es zu Situationen kommt, durch
 die Gefahr fr Konsistenz und Integritt  eines Programmes oder Betriebssys-
 tems besteht, bzw. wenn die CPU einfach nicht mehr weiterarbeiten knnte.

 In solchen Situationen werden die ISRs festgelegter Interrupts angesprungen,
 die sich dann  dem entsprechenden Fehler annehmen sollen.  Was die jeweilige
 ISR macht, hngt 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
 fr CPU-interne Zwecke  (wie z.B. Exception Handling) definierte,  sich aber
 z.B. IBM nicht  um diese Konvention  kmmerte.  IBM benutzt diese Interrupts
 gleichzeitig  fr gnzlich  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  zustzlich noch  in drei  verschiedene
 Kategorien unterteilt:


    Faults  Returnadresse (CS:EIP) auf dem Stack zeigt auf die verursachende
             Instruction fr den Fault.

    Traps   Returnadresse zeigt hinter die verursachende Instruction fr die
             Trap.

    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. Betroffen sind davon die Exceptions 8, 10, 11, 12 und 13:


   Fehlercode bei PM Exceptions mit Segmentbezug
   
   Ŀ
   1111111111111111                
   fedcba9876543210fedcba9876543210
                                                              
                                 Obere 14 Bit des von der  TIE
            reserviert           Exception betroffenen     IDX
                                 Segment Selectors         DTT
   

    EXT = 1, wenn Exception-Ursache auerhalb 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 Interrupt 14 wird zwar auch ein Fehlercode auf den Stack geschoben,  der
 hat jedoch ein anderes Format (Beschreibung siehe Interrupt 14).

 Im folgenden eine Auflistung,  welche Ursachen die Auslsung einer Exception
 haben kann und was aus meiner Sicht sonst noch ber den jeweiligen Interrupt
 wissenswert erscheint:


 Interrupt 0, Divide Error (PM: Fault)
 

    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)


 Interrupt 1, Single Step (86+) bzw. Debugging Exceptions (386+)
 

    Single Step (PM: Trap)

     Wird durch  Setzen des TF (Trap-Flag)  ausgelst. TF wird beim Einsprung
     in  die ISR gelscht (Trap-Handler  wrde sonst selbst im Einzelschritt-
     verfahren durchlaufen).

     Sollten gleichzeitig Hardware-Interrupts (IRQ 0..15) anliegen, sorgt die
     interne Priorittenverteilung  dafr,  da erst Returnadresse  und Flags
     von Interrupt 1  auf den Stack gelegt werden,  dann das TF gelscht wird
     und im Anschluss daran die ISR des IRQ gerufen wird.  Die ISR von Inter-
     rupt 1 erhlt die Kontrolle erst nach Abarbeitung etwaiger IRQ-ISRs.

    Data Address Breakpoint (PM: Trap)

     Break-Condition erfllt.

    Instruction Address Breakpoint (PM: Fault)

     Break-Condition erfllt.

    General Detect (PM: Fault)

     Nur Intel's ICE: Nchste Instruction wrde auf ein Debugregister zugrei-
     fen, da momentan von ICE benutzt wird.

    Taskswitch Breakpoint Trap

     Break-Condition erfllt.


 Interrupt 2, Non-maskable Interrupt (NMI)
 

   Zhlt nicht zu den herkmmlichen Exceptions. Wird ausgelst, wenn der NMI-
   Pin durch Hardware angesprochen wird.

   Vom Verhalten her  knnte man NMIs  als Faults  bezeichnen  (Returnadresse
   zeigt auf verursachende Instruction).

   Sollten whrend  der ISR-Abarbeitung weitere NMIs auftreten,  werden diese
   bis zum IRET ignoriert. Ab 286+  wird jedoch  zumindest ein  nachfolgender
   NMI vermerkt und nach erfolgtem IRET direkt behandelt.

   Sollten die maskierbaren IRQs in der NMI-ISR nicht mittels CLI unterbunden
   worden sein, knnen diese die NMI-ISR natrlich unterbrechen.


 Interrupt 3, Breakpoint (PM: Trap)
 

   Dieser Interrupt  wird durch die Instruction INT 3 (Opcode ccH) ausgelst.

   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 knnen.


 Interrupt 4, INTO Overflow Detected (PM: Trap)
 

   Dieser Interrupt wird ausgelst,  wenn das OF-Flag (Overflow Flag) gesetzt
   ist, whrend die Instruction INTO (Opcode ceH) ausgefhrt wird.

   Dadurch erhlt  der Programmierer  die Mglichkeit,  das Overflow-Handling
   seines Programmes in eine ISR auszulagern und kann sich innerhalb des Pro-
   grammes speicherintensive Gerste von JO/JNO/JMP Instructions sparen.


 Interrupt 5, BOUND Range exceeded, 186+, (PM: Fault)
 

   Dieser Interrupt wird von der Instruction BOUND ausgelst,  wenn ein ber-
   prfter Index  (signed Word bzw. DWord (386+))  auerhalb  der angegebenen
   Grenzen liegt.

   BOUND eignet sich also z.B. bestens, um Range Checks fr Array-Zugriffe zu
   realisieren.


 Interrupt 6, Invalid Opcode, 286+, (PM: Fault) [#UD]
 

    Ein ungltiger Opcode wurde ausgefhrt.

    Ein gltiger Opcode  wurde mit ungltigem Operanden ausgefhrt (z.B. LDS
     oder LES mit Register als Quelle).

    Das LOCK Prefix  wurde zusammen  mit einer  nicht zulssigen Instruction
     benutzt (386+).

   Dieser Interrupt  wurde frher  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 Ausfhrung vor, aber in CR0
     (bzw. MSW) ist das EM-Bit (Emulation) gesetzt.

    Dem Prozessor liegt eine ESC  oder WAIT Instruction  zur Ausfhrung vor,
     gleichzeitig  sind  aber  MP-Bit (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 ausgelst,  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, Debugging 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    Contributory
     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


   Die  in der nachfolgenden Tabelle aufgefhrten Klassenkombinationen fhren
   schlielich 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 knnen die Exceptions vom Prozessor
   einfach nacheinander abgearbeitet werden.

   Btw: Obwohl der Double Fault von Intel als Abort klassifiziert wird, zeigt
   die Returnadresse auf dem Stack trotzdem auf die auslsende Instruction.

   Wird ein Double Fault ausgelst,  schiebt der Prozessor zwar einen Fehler-
   code auf den Stack  der Double Fault ISR,  aber der ist  leider immer Null
   und somit imo vllig berflssig...

   Durch Forcieren  einer weiteren Exception innerhalb einer Double Fault ISR
   lsst sich ein sogenannter "Triple Fault" auslsen. Bei einem Triple Fault
   geht der Prozessor in den ShutdownMode,  d.h. es wird ein CPU-Reset ausge-
   lst, wobei der Prozessor auch in den Real Address Mode zurckkehrt...


 Interrupt 9, Coprocessor Segment Overrun, 286+, (PM: Abort)
 

   Dieser Interrupt wird ausgelst,  wenn der Coprozessor versucht, auf einen
   Speicherbereich auerhalb Segment- bzw. Page-Grenzen zuzugreifen.

   Ab 486+ wird diese Exception  nach Interrupt 13 (General Protection Fault)
   umgeleitet.

   Solange  der Coprozessor  nicht resetted  worden ist, kann nur noch FNINIT
   benutzt werden.  Jeder andere FPU-Befehl fhrt garantiert zum Hang, da die
   FPU noch auf Daten wartet, die nie kommen werden... ;)


 Interrupt 10, Invalid Task State Segment, 286+, PM only (Fault) [#TS]
 

   Dieser Interrupt  wird ausgelst,  wenn whrend eines Taskswitch auf einen
   Task mit ungltigem TSS (Task State Segment) umgeschaltet wird. Ein ungl-
   tiges TSS kann verschiedene Ursachen haben:


      TSS Limit ist kleiner als 43 (286) bzw. 103 (386+)

      Ungltiger LDT Selector oder LDT nicht vorhanden

      CS, DS, ES, FS, oder GS Selector auerhalb 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 knnen sowohl im alten,  als auch in einem neuen Task
             ausgelst werden,  je nachdem,  ob der Taskwechsel bereits voll-
             stndig vollzogen wurde, oder nicht.

             Solange das TR (Task Register) nicht entsprechend upgedatet ist,
             wird die Exception im alten Task ausgelst.

   Um sicherzustellen,  da die Interrupt 10 ISR ein gltiges TSS hat, sollte
   die ISR vorzugsweise  selbst als Task betrieben werden,  der via Task Gate
   ausgelst werden kann. Und natrlich darf die ISR vor dem Rcksprung nicht
   vergessen, das Busy Bit des neuen Tasks zu lschen... ;)


 Interrupt 11, Segment not present, 286+, PM only (Fault) [#NP]
 

   Interrupt 11 wird ausgelst, wenn der Prozessor merkt, da das Present-Bit
   eines Descriptors gleich Null ist. Das kann passieren:


      beim Versuch einen Gate Descriptor zu benutzen.

      beim Versuch CS, DS, ES, FS oder GS zu laden.  Das gilt brigens nicht
       fr SS, in diesem Fall wrde ein Stack Fault (Interrupt 12) ausgelst.

      beim Versuch  mit LLDT den LDT zu laden,  es sei denn,  dies geschieht
       whrend einem Taskswitch.  Dann wrde Invalid TSS Fault (Interrupt 10)
       ausgelst.


   Der Prozessor schiebt vor der Auslsung einen Fehlercode auf den Stack.

   Bei diesem Fault kann die Programmausfhrung  trotzdem fortgesetzt werden,
   wenn die Interrupt 11 ISR das Present-Bit setzt und zurckspringt.

   Allerdings ist  dabei zu beachten,  da der Fault  auch whrend eines noch
   nicht abgeschlossenen Taskswitches  ausgelst worden sein kann.  In diesem
   Fall knnte es sein, da der Prozessor noch nicht alle Segmentregister auf
   ihre Gltigkeit geprft hat und nachfolgende Speicherzugriffe irgendwo ins
   Nirwana laufen. Folglich sollte eine Interrupt 11 ISR alle Segmente selbst
   auf Gltigkeit prfen,  bevor sie das Present-Bit setzt und dem neuen Task
   wieder die  Kontrolle bergibt.  Anderenfalls kann  es spter  zu kuriosen
   General Protection Faults kommen,  deren Ursachen viel schwerer zu lokali-
   sieren sind.

   Der einfachste Weg die Zulssigkeit der Segmentregister zu prfen, ist ein
   simples PUSH/POP mit jedem Register,  da der Prozessor nur POPs mit glti-
   gen Daten in ein Segmentregister zulsst.

   Ein anderer Weg ist es,  die Interrupt 11 ISR  als eigenstndigen Task  zu
   betreiben. Der durch den Rcksprung aus der ISR ausgelste Taskswitch erle-
   digt alle erforderlichen Prfungen.


 Interrupt 12, Stack Fault, 286+, (PM: Fault) [#SS]
 

   Im Real und V86 Mode wird Interrupt 12 ausgelst,  wenn versucht wird, auf
   ein Word an Adresse SS:ffffH zuzugreifen.

   Im Protected Mode kann Interrupt 12 aus zwei Grnden ausgelst werden:


      Limit-Verletzung  durch eine Instruction die auf das SS zugreift, d.h.
       die Instruction fhrt zu einem  Unter- oder berlauf des Stacks,  oder
       greift  auerhalb  des gltigen Stackbereiches zu.  Zu den auslsenden
       Instructions zhlen somit PUSH/POP, ENTER/LEAVE und MOV.

      Present-Bit des SS-Descriptors ist gleich Null.  Erkannt wird dies bei
       einem Task Switch, CALL, RET, LSS, sowie bei MOV/POP nach SS.


   Der Prozessor  schiebt auch bei diesem Interrupt  einen Fehlercode auf den
   Stack. Bei Present-Bit gleich Null  oder CALLs  die zu einem Stackberlauf
   fhren ist der fragwrdige Selector im Fehlercode enthalten, ansonsten ist
   der Fehlercode gleich Null.

   HINWEIS: Normalerweise zeigt die Returnadresse bei Faults ja immer auf die
            auslsende Instruction.  Wurde der Stack Fault aber whrend eines
            Task Switch aufgrund Present-Bit gleich Null ausgelst, zeigt die
            Returnadresse bereits auf die erste Instruction des neuen Tasks!

   Bei der Auslsung wegen Present-Bit gleich Null whrend eines Task Switch,
   kann  die Programmausfhrung  wieder aufgenommen werden,  wenn die ISR das
   Present-Bit setzt und zurckspringt.  Vor einem Resume sind allerdings die
   gleichen Dinge  zu beachten (Segmentprfung),  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 ausgelst:


      OperandWrap: Eine Instruction versucht auf einen Speicheroperanden zu-
       zugreifen,  der teilweise in einem  und teilweise  im nchsten 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 nchsten Segment.


   Im Protected Mode kann die Auslsung Interrupt 13 viele Ursachen haben, da
   alle Schutzverletzungen,  die nicht selbst  durch eine Exception gecovered
   werden, hier zusammenlaufen:


      Versucht in ein ReadOnly-/ExecOnly-Segment zu schreiben.

      Versucht in einem ExecOnly-Segment zu lesen.

      Versucht Code in einem NonExec-Segment auszufhren.

      Versucht Speicherzugriffe ber DS, ES, FS oder GS auszufhren, wenn
       das Segmentregister einen Null-Selector enthlt.

      Versucht DS, ES, FS oder GS mit dem Descriptor eines Exec-Segmentes
       zu laden, das nicht auch lesbar ist.

      Versucht SS mit dem Descriptor eines Exec-Segmentes zu laden.

      Versucht SS mit einem ReadOnly-Descriptor zu laden (auer es handelt
       sich um einen TSS-Selector whrend eines Task Switch, dann kommt es
       zu einem Interrupt 10).

      Versucht SS, DS, ES, FS oder GS mit dem Descriptor eines System-Seg-
       mentes zu laden.

      Versucht CR0 mit PG-Bit = 1 und PE-Bit = 0 zu setzen.

      Limit-Verletzung beim Referenzieren eines Descriptor Tables.

      Limit-Verletzung beim Einsatz von CS, DS, ES, FS oder GS.

      Umschalten auf einen beschftigten (busy) Task.

      Verletzung der Privileg-Regeln.

      V86 Mode: Interrupt/Exception wurde ber Trap/Interrupt Gate auf einen
       CPL <> 0 ausgelst.

      Instruction zu lang (286 > 10 Bytes, 386+ > 15 Bytes).


   Der Prozessor  schiebt auch bei diesem Interrupt  einen Fehlercode auf den
   Stack.  Sollte das Laden  eines Descriptors den Interrupt ausgelst haben,
   ist der entsprechende Selector im Fehlercode enthalten,  ansonsten ist der
   Fehlercode gleich Null.


 Interrupt 14, Page Fault, 386+, PM/V86 Only (Fault) [#PF]
 

   Dieser Interrupt wird ausgelst,  wenn das PG-Bit in CR0 gesetzt ist,  und
   die CPU  whrend  der bersetzung  einer Adresse  (linear in physikalisch)
   einen der folgenden Umstnde bemerkt:


      Der anfordernde Code  hat eine  zu niedrige Privilegstufe,  um auf die
       gewnschte Page zugreifen zu drfen.

      Das Present-Bit des Page Directory  bzw. des Page Table Eintrages, der
       bersetzt werden soll, ist gleich Null.


   Der Prozessor  schiebt auch bei diesem Interrupt  einen Fehlercode auf den
   Stack. Allerdings weicht der Aufbau vom blichen Fehlercode ab:


     Fehlercode bei Page Fault
     
     Ŀ
     1111111111111111                
     fedcba9876543210fedcba9876543210
                                                                 
                                                              PWU
                            reserviert                        P//
                                                              RRS
     

      U/S = 0, wenn Page Fault im SupervisorMode auftrat
            1, wenn Page Fault im UserMode auftrat
      W/R = 1, wenn Schreibzugriff den Page Fault ausgelst hat
            0, wenn Lesezugriff den Page Fault ausgelst hat
      PPR = 1, wenn Page Fault durch zu niedrige Privilegstufe
            0, wenn Page Fault durch Present-Bit gleich Null


   Wird ein Page Fault ausgelst,  knnen zustzlich  die Controlregister CR2
   und CR3 ausgelesen werden,  um weitere Infos zu erhalten.  Siehe dazu auch
   Topic BAW0007 (CPU-Registersatz).

   HINWEIS 1:

     Wenn  ein Betriebssystem  aufgrund  seiner Programmierung  dies zulsst,
     kann ein Page Fault auch whrend eines Task Switch ausgelst werden. Die
     Auslsung 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 fr neuen Task)
        Lesen des neuen TSS (um die Segment-Descriptoren zu prfen)
        Lesen der LDT (Besttigung der neuen Segmentregister)


     In den  ersten beiden Fllen  zeigt die Returnadresse auf dem Stack noch
     auf die Instruction,  die den Task Switch verursachte, in den beiden an-
     deren Fllen bereits auf die nchste auszufhrende Instruction des neuen
     Task.

     Um rger  zu vermeiden,  empfiehlt es sich daher imo,  Interrupt 14 ISRs
     immer als eigenstndige Tasks zu betreiben,  die via Task-Gate ausgelst
     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 auslst.  Dadurch verliert  der Stackpointer SS:SP
     (oder auch SS:ESP) natrlich seine Integritt.

     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 natrlich die wunder-
     samsten Ergebnisse zu Tage bringen kann... ;)

     Sicherer ist imo auch hier wieder  der Betrieb  der Interrupt 14 ISR als
     eigenstndiger Task,  oder wenn  Trap/Interrupt Gates vorgezogen werden,
     die Initialisierung des Stack ber die Instruction LSS.


 Interrupt 16, Coprocessor Error, 286+, (PM: Fault) [#MF]
 

   Dieser Interrupt wird ausgelst, wenn am ERROR# Pin der CPU ein Signal vom
   Coprozessor anliegt.  Der Pin wird von der CPU  nur zu Beginn der Instruc-
   tions ESC und WAIT abgefragt (WAIT nur, wenn EM-Bit in MSW bzw. CR0 gleich
   Null ist).



 === BAW0018 ================================================================

 Hochsprachen-Schnittstellen

 a) TASM <-> TP/BP, Lennart Groetzbach 2:2432/321.13:



 === BAW0019 ================================================================

 VGA-Registersatz

 Juergen Thelen 2:2450/645.5:



 === ALG0001 ================================================================

 DWord -> DezStr (Zahlenkonvertierung)

 Markus Krieger 2:2454/420.6:



 === ALG0002 ================================================================

 DWord -> HexStr (Zahlenkonvertierung)

 Markus Krieger 2:2454/420.6:



 === ALG0003 ================================================================

 DWord -> BinStr (Zahlenkonvertierung)

 Markus Krieger 2:2454/420.6:

 Ein DWord besteht aus 32 Bit.  Will man nun diesen Wert in einen Binrstring
 umformen,  bentigt man  fr die Speicherung  des Strings dementsprechend 32
 Bytes.

 Fr eine Umformung in einen Binrstring eignen sich die sogenannten Schiebe-
 befehle (man braucht sie nicht unbedingt,  doch da dies hier  eine Assembler
 FAQ ist, gehe ich besonders darauf ein).

 Ein Schiebebefehl  schiebt alle Bits  in einer Speicherzelle nach links oder
 rechts. So kann man,  wenn man z.B. die Bitfolge 11001010 hat,  diese um ein
 Bit nach links oder rechts schieben:

 urspruenglich   11001010   daraus wird nach einer Verschiebung
 nach links      10010100   bzw.
 nach rechts     01100101

 Man sieht allerdings, da bei einer Verschiebung unter Umstnden eine 1 weg-
 fllt  bzw. auf der anderen Seite wieder auftaucht (ist abhngig vom verwen-
 deten Schiebebefehl). Benutzt man z.B. auf einem PC den Befehl SHL, wird das
 herausfallende Bit im CF (Bit 0 im [E]FLAGS-Register) gespeichert.

 Abhngig vom Inhalt des CF knnte man folglich nach jedem Schiebevorgang das
 CF prfen und sein Programm entsprechend verzweigen lassen, um eine 0 oder 1
 in seinen String zu schreiben.

 Um eine vollstndige Umformung zu erhalten, mu der Vorgang 32mal wiederholt
 werden, ein DWord  hat ja schlielich 32 Bit.  Natrlich darf man auch nicht
 vergessen, immer die Schreibposition im  ASCII-String jeweils um eine Stelle
 zu erhhen... ;)

 Nochmal kurz:

 1. Man setzt einen Zhler auf 32 (Anzahl der Durchlufe)
 2. Man fhrt einen Schiebebefehl nach links aus
 3. In einem Register landet das Bit, welches nach links herausgeschoben
    worden ist
 4. Je nach Zustand dieses Bits ASCII-0 oder 1 in den String schreiben
 5. Die Schreibposition des nchsten Zeichens im String um eins erhhen
 6. Den Zhler um eins vermindern
 7. Wenn der Zhler = 0 ist, ist die Umformung abgeschlossen, ansonsten
    weiter bei 2.



 === ALG0004 ================================================================

 DezStr -> DWord (Zahlenkonvertierung)

 Markus Krieger 2:2454/420.6:



 === ALG0005 ================================================================

 HexStr -> DWord (Zahlenkonvertierung)

 Markus Krieger 2:2454/420.6:



 === ALG0006 ================================================================

 BinStr -> DWord (Zahlenkonvertierung)

 Markus Krieger 2:2454/420.6:



 === ALG0007 ================================================================

 InsertionSort (Sortierung)

 Michael Klose 2:2446/301.7:



 === ALG0008 ================================================================

 BubbleSort (Sortierung)

 Michael Klose 2:2446/301.7:



 === ALG0009 ================================================================

 QuickSort (Sortierung)

 Michael Klose 2:2446/301.7:



 === ALG0010 ================================================================

 MergeSort (Sortierung)

 Michael Klose 2:2446/301.7:



 === ALG0011 ================================================================

 HeapSort (Sortierung)

 Michael Klose 2:2446/301.7:



 === ALG0012 ================================================================

 ShellSort (Sortierung)

 Michael Klose 2:2446/301.7:



 === ALG0013 ================================================================

 BucketSort (Sortierung)

 Michael Klose 2:2446/301.7:



 === ALG0014 ================================================================

 Boyer-Moore (Suche)

 === ALG0015 ================================================================

 Lempel-Ziv (Komprimierung)

 === ALG0016 ================================================================

 Lempel-Ziv-Welch (Komprimierung)

 === ALG0017 ================================================================

 Bresenham Line (Grafik)

 === ALG0018 ================================================================

 Bresenham Circle (Grafik)

 Michael Klose 2:2446/301.7:

 Vorwort:
 --------

 Dieses Topic  der FAQ  beschreibt den  Bresenham  Kreisalgorithmus  in einer
 ziemlich mathematischen Weise, so da man nachvollziehen kann, warum er denn
 berhaupt funktioniert.

 Der Herr Bresenham  hat einen  ziemlich genialen  Algorithmus entwickelt, um
 mit  mglichst wenig  Rechenaufwand Kreise auf dem Bildschirm zu zaubern. Es
 wird nur  mit ganzen Zahlen gerechnet,  und es werden keine Multiplikations-
 oder Divisionsbefehle benutzt.  Addition, Subtraktion und Schiebeoperationen
 sind die einzigen bentigten Operationen.

 Wer nur wissen mchte, wie der Algorithmus lautet,  und sich fr die Herlei-
 tung nicht interessiert, braucht sie sich ja auch nicht angucken - im Endef-
 fekt ist ja  nur wichtig,  was hinten rauskommt.  Ich habe mich damals dafr
 interessiert, wie er berhaupt funktionieren kann, das erkennt man am Ender-
 gebnis ja nicht gerade. :-)

 Dennoch habe  ich versucht,  die Herleitung  so verstndlich  wie mglich zu
 machen, so da jeder mitkommen kann. Um den Bresenham-Algorithmus zu verste-
 hen, muss man ein paar Kenntnisse in Termumformung haben, ein bichen Pytha-
 goras, aber das reicht in Prinzip schon.  Pythagoras habe ich aber auch ver-
 sucht noch mal kurz zu erlutern.

 So, ich wnsche noch viel Spass!

 Michael Klose

 Internet: mklose@uni-duisburg.de
 Fido    : 2:2446/301.7



 Wie zeichnet man einen Kreis?
 -----------------------------

 Mchte man einen Algorithmus programmig umsetzen, so fragt man sich meist-
 ens, wie wrde man es denn auf Papier machen?   Ich wei nicht, ob es andere
 auch so machen, aber das ist wenigstens meine Art.

 Ich berlege  immer Schritt fr Schritt,  wie ich es denn  ueberhaupt mache.
 Meistens  sind die Sachen  fr uns  aber so logisch, da man gar nicht nach-
 denkt, wie man es denn gemacht hat (Beispiel: Sortierung von Karten).

 So, jetzt wie zeichnet man einen Kreis?  Man nimmt einen Zirkel,  presst die
 Spitze in das Papier rein, und zieht mit ihm einen Kreis. Hier dran kann man
 die ersten beiden Eigenschaften eines Kreises festhalten:

   * Er besitzt einen konstanten Mittelpunkt

   * Alle 'Punkte' des Kreises haben einen festen (den gleichen) Abstand von
     diesem Mittelpunkt.

 Mit diesen  beiden Eigenschaften alleine,  lsst sich am Computer leider nur
 schwer  ein Kreis zeichnen,  zumindest nicht,  wenn die  einzige Mglichkeit
 darin besteht,  ihm zu sagen,  da er an Position x,y ein Punkt setzen soll.
 Unser Problem ist, wie bringen wir es ihm bei?

 Ein ganz wichtiger Punkt ist die Symmetrie - er hilft uns das Problem zumin-
 dest auf einen  kleinen Teil des Kreises zu reduzieren.  Wenn man sich einen
 Kreis genauer betrachtet, kann man sehen, da man im Prinzip nur ein 1/8 des
 Kreises berechnen mu, der Rest folgt aus der Symmetrie:

   W2       W1
   \   |   /             \ P1|P  /
    \  |  /               \ *|* /
     \ | /            P2 * \ | / *P7
      \|/                   \|/
   ----*----  x          ----*----
      /|\                   /|\
     / | \            P3 * / | \ *P6
    /  |  \               / *|* \
   /   |   \             / P4|P5 \
       y

 Dieses Kreuz zeigt alle Spiegelachsen, die x und die y Achse, sowie die bei-
 den Winkelhalbierenden.  In der Zeichnung rechts  sieht man,  da wenn man P
 berechnet hat, man somit gleichzeitig an 8 weitere Punkte des Kreises kommt:
 P wurde immer  gegen den Uhrzeigersinn  gespiegelt -  zuerst an der y-Achse,
 dadurch wurde  Punkt P1  gewonnen,  dann wurde P1 an W2 gespiegelt,  hieraus
 wurde P2 gewonnen, etc...

 Man kann aber auch sehen, da P3 z.B. P6 entspricht,  wenn man ihm auf der y
 Achse spiegelt.

 In Koordinaten ausgedrckt, ergibt sich somit:

   P:	( +x, +y )
   P1:	( -x, +y )
   P2:	( -y, +x )
   P3: 	( -y, -x )
   P4: 	( -x, -y )
   P5: 	( +x, -y )
   P6: 	( +y, -x )
   P7: 	( +y, +x )

 Was wir jetzt gewonnen haben, ist folgendes: Wenn man alle x,y Werte fr 1/8
 des Kreises kennt, so kennt man auch alle weitere Punkte des Kreises.


 Berechnungsverfahren fr x,y
 ----------------------------

 Das Problem ist jetzt,  wie kommt man  an die x,y Werte?  Das ist  eine gute
 Frage! :-)

 Naja, betrachten wir noch mal den Kreis:  Man kann durch einfaches hingucken
 erkennen,  da es fr jeden x-Wert mindestens einen y-Wert geben mu (wenn x
 nicht auerhalb des Kreises ist).

 Zustzlich kennt man  folgende Koordinaten (ebenfalls ermittelt durch hingu-
 cken):

   (  0,  R )
   (  R,  0 )
   ( -R,  0 )
   (  0, -R )

 Das ist schon eine ganze Menge.

 Wenn wir schon  alle Eigenarten des Kreises aufzhlen,  dann sollte man noch
 erwhnen, da er 360 (Grad) hat, so, jetzt sind wir fast vollstndig.

 Nehmen wir mal einen x-beliebigen Kreispunkt:

                                |
                                |
                                |
                         y-Wert |----------------------------*P
                                |                      ----- |
                                |             r  ------      |
                                |           -----)           |
                                |     ------     )          _|
                                |-----  Alpha    )         |o|
   -----------------------------++-------------------------------------
                                |                            x-Wert
                                |
                                |
                                |

 Eingezeichnet ist der Punkt P,  der x-Wert, der y-Wert und der Winkel Alpha.
 Wie man sieht,  spannt der  Koordinatenursprung (0,0),  der Punkt P  und der
 Punkt (x,0) ein rechtwinkliges Dreieck ein. Rechtwinklig ist ganz wichtig!!!
 Der Rechte Winkel ist eingezeichnet.


 Der Satz des Pythagoras
 -----------------------

 Wer ihn kennt, kann direkt zum nchsten Abschnitt gehen.

 Zur Erinnerung:  Die Kurzform lautet a + b = c,  wobei c die Hyphothenuse
 und a und b die Katheten sind.

 Viele verstehen jetzt Bahnhof: Es ist ganz einfach:

   a,b : Katheten
   c   : Hypothenuse                                         B
                                                             *
                                                       ----- |
                                             c   ------      |
                                            -----)           |a
                                      ------     )          _|
                                 -----  Alpha    )         |o|
                              A +-----------------------------C
                                              b

 Das Dreieck hat 3 Grundseiten:  a, b und c.  In einem Rechtwinkligen Dreieck
 bezeichnet man  die Grundseiten,  die den rechten Winkel (90) einschlieen,
 als Katheten (hier a,b) und die verbliebene Seite c als Hypothenuse.

 Betrachtet man den Winkel Alpha, so ist a ja die Kathete gegenber, deswegen
 bezeichnet man a  (in Bezug auf den Winkel Alpha),  als Gegenkathete - b ist
 die sogenannte Ankathete, weil sie direkt AN dem Winkel Alpha liegt.

 Der Herr Pythagoras hat irgendwie herausgefunden,  da das a + b = c ist,
 so einfach!

 Wo ich schon dabei bin,  hat jetzt mit dem eigentlichen Thema nichts zu tun,
 aber was tut man nicht fr die Bildung :-) - komme ich mal auf Sinus und Co-
 sinus:

 Es gilt:

 Sinus Alpha ist gleich  der Lnge der Gegenkathete dividiert durch die Lnge
 der Hyphotenuse:

   sin(Alpha) = a/c

 Cosinus Alpha ist gleich  der Lnge der Ankathete  dividiert durch die Lnge
 der Hyphotenuse:

   cos(Alpha) = b/c

 Dann gibt es noch Tangens Alpha:

   tan(Alpha) = Gegenkathete/Ankathete.

 Warum ich das erwhne?  Weil man hufig  Lsungen fr einen Kreis sieht, die
 einfach fr Alpha eine FOR Schleife von 0 bis 360 Grad laufen lassen, und so
 die Punkte x,y berechnen:

   Es gilt ja:

     sin(Alpha) = a/c

   Umgeformt ergibt das:

     a = c * sin(Alpha)

   Die Hypothenuse ist aber gerade unser Radius. Also gilt:

     y = r * (sin Alpha)

   Weiterhin gilt:

     cos(Alpha) = b/c

   oder umgeformt:

     b = c * cos(Alpha)

   oder hier bei uns im speziellen Fall:

     x = r * cos(Alpha)

   Somit hat man die Punkte x und y:

   (r * cos(Alpha), r * sin(Alpha))

   r ist konstant (Radius), und Alpha wird aus der FOR-Schleife entnommen.

 So kann man also ohne Probleme einen Kreis zeichnen. Warum macht man es dann
 nicht?  Erstens geht es hier  um den Bresenham Algorithmus,  und 2. muss man
 bedenken, da Sinus und Cosinus  aufwendig zu berechnen  und zudem auch noch
 Fliekommazahlen sind (also keine Ganzzahlen, sondern Zahlen mit Brchen).

 Mit Fliekommazahlen kann der Rechner nicht so schnell umgehen, wie mit gan-
 zen Zahlen.  Um es einfach auszudrcken:  die Methode  einen Kreis mit Sinus
 und Cosinus zu berechnen, ist verdammt langsam!!


 Anwendung des Satzes des Pythagoras
 -----------------------------------

 Betrachten wir noch einmal unser Diagramm:

                                |
                                |
                                |
                         y-Wert |----------------------------*P
                                |                      ----- |
                                |             r  ------      |
                                |           -----)           |
                                |     ------     )          _|
                                |-----  Alpha    )         |o|
   -----------------------------++-------------------------------------
                                |                            x-Wert
                                |
                                |
                                |

 Wir haben gesagt, da es zu jedem x ein y geben muss.  X tun wir nun in eine
 FOR-Schleife und lassen x von 0 bis r laufen.  Wenn wir alle y Werte berech-
 net haben, haben wir ein  Kreis. Es reicht zwar,  wie wir schon gesehen ha-
 ben, 1/8 des Kreises zu berechnen, um einen Kreis zu zeichnen,  fr das Ver-
 stndnis gehe ich aber hier von einem 1/4 Kreis aus.

 Schauen wir uns jetzt mal das rechtwinklige Dreieck genauer an:

 x und y sind die Lngen der Katheten,  der Radius ist die Lnge der Hyphote-
 nuse.  Warum r?  Weil es eine Eigenschaft des Kreises ist, da die Lnge der
 Verbindungslinie  zwischen einem beliebigen Kreispunkt  und dem Kreismittel-
 punkt konstant ist, und dieses der Radius des Kreises ist.

 Man erinnere sich  an den Zirkel -  der Mittelpunkt ist fest,  und die Blei-
 stiftspitze stellt den Punkt dar. Der Abstand zwischen Kreismittelpunkt (Na-
 del) und Bleistiftspitze (Punkt) bleibt immer konstant.

 Also, Pythagoras war: a + b = c

 Hier ist a = y, b = x und c = r, also:

   y + x = r

 Umgeformt:

   y = r - x

 Um jetzt von y zu y zu kommen, mu man auf beiden Seiten die Wurzel ziehen.

 Also gilt:

   y = Wurzel von (r - x)

 Jetzt hat man eine Methode gefunden,  um einen 1/4 Kreis zu zeichnen - damit
 auch den gesamten. R mu nur einmal ausgerechnet werden, da der Radius sich
 ja nicht ndert.  X jedoch  mu man fr jedes x der Schleife neu berechnen,
 ebenso die entstehende Wurzel.

 Es gibt raffinierte Verfahren,  um die Wurzel einer Zahl zu ziehen. Program-
 miert man diese selber,  und benutzt man dabei nur ganze Zahlen,  so hat man
 schon eine verdammt schnelle Kreisroutine.

 Es geht aber noch schneller, und zwar mit dem Bresenham Algorithmus.


 Grundideen des Bresenham Algorithmus
 ------------------------------------

 Bresenham hat sich folgendes berlegt:

 Hat man einen Punkt des Kreises (z.B. (0,y)), so mu auf einem Computerbild-
 schirm  der nchste Punkt  entweder den gleichen,  oder einen um eins unter-
 schiedlichen y Wert haben.  Folgende Zeichnung macht dieses ungefhr fr ei-
 nen 1/4 Kreis deutlich:

   ***
      **
        *
         *

 Bresenham geht jetzt  Punkt zu Punkt hin und sagt,  welcher Fehler  ist denn
 jetzt grer?  Wenn ich auf der  aktuellen y Position bleibe,  oder wenn ich
 einen niedriger gehe?

 Das kann man ausrechnen:

 y sei jetzt die Position auf dem Bildschirm,  yr sei der tatschliche y-Wert
 fr diesen x-Wert.  yr kann man ja aus x errechnen (z.B. mit Pythagoras), es
 gilt:

   yr = Wurzel (r - x)

 Man prft jetzt, ob y oder y-1 naeher an yr liegt.


 ----------------------------------
 [Einschub: Ausrechnen von Fehlern]

   Wie vergleicht man, welche Zahlen nher zusammen liegen?

   Nehmen wir ein einfaches Beispiel: Liegt 7 oder 4 nher an 5? Fr Menschen
   ist das ganz klar: 4 - aber wie kommen wir darauf?

   Die Zahl, womit wir vergleichen, ist 5.  Jetzt ziehen wir einmal  die eine
   Zahl von 5 ab, und einmal die andere, und betrachten dann die resultieren-
   den Betrge, also das Ergebnis ohne Vorzeichen:

     5 - 7 = -2
     5 - 4 =  1

   Logisch: 1 ist kleiner als 2, also liegt 4 nher an 5 dran.

   Man betrachtet  bei dieser Methode  den sogenannten Fehler  den man macht,
   wenn man 4 bzw. 7 statt 5 nimmt. Ist der Fehler kleiner, so liegt die Zahl
   nher dran.

   Das Problem sind  die Vorzeichen:  Aber wir knnen uns  da was schnes aus
   der Mathematik zunutze machen:  ist der Betrag von a grer als der Betrag
   von b, so ist auch a grer als b. Das ist eine mathematische Tatsache!!
   Nochmal:

     Aus |a| < |b|   folgt a < b

   Also hier:

     (5-7) = (-2) = 4
     (5-4) = ( 1) = 1

   Wie vorhin: Auch hier ist 1 ist kleiner als 4, also liegt 4 nher an 5.

 [Ende des Einschubs]
 --------------------


 yr war der tatschliche y Wert.

 Nach Pythagoras gilt:

   yr = r - x bzw. yr = Wurzel (r - x)

 Wir betrachten jetzt  zwei Fehler: Fehler1 ist,  wenn wir bei der jetzigen y
 Position bleiben,  und Fehler2 ist der Fehler,  den man macht,  wenn man den
 nchsten Punkt um einen Pixel niedriger zeichnet (y-1).

 Verwendet man Quadrate,  ist yr auch immer eine ganze Zahl.  Das ist wichtig
 fr die sptere Implementierung!!!

 Fehler1 = y . yr bzw. yr eingesetzt:

   Fehler1 = y - r + x

 Fr Fehler2 gilt:

   Fehler2 = yr - (y-1)

 mit yr eingesetzt:

   Fehler2 = r - x - y -+ 2y - 1

 Wir ziehen hier umgekehrt ab,  weil wir zum Vergleichen einen positiven Wert
 haben mchten.  Es ist einfacher  als spter den Betrag zu ziehen,  oder gar
 die Gleichungen zu quadrieren!!!


 ------------------------------
 [Einschub: Binomische Formeln]

   Fr die Berechnung von (y-1) wurde eine binomische Formel benutzt.

   Es gibt folgende binomische Formeln:

     (a + b) = a + 2ab + b
     (a - b) = a - 2ab - b
     (a+b)(a-b) = a - b

   Das ist einfach ein Sachverhalt, der gilt.  Man kann ihn auswendig lernen.
   Nachvollziehen kann man das leicht an einem Beispiel. Will man z.B. 92 zum
   Quadrat im Kopf ausrechnen, so kann man folgendes machen:

   (90 + 2) = 90 + 2*90*2 + 2 = 8100 + 360 + 4 = 8464

   Stimmt hoffentlich, ich habe es nicht nachgerechnet!!!!

 [Ende des Einschubs]
 --------------------


 Wo waren wir?

 Fehler1 = y . yr  bzw. yr eingesetzt:

   Fehler1 = y - r + x

 Fuer Fehler2 gilt:

   Fehler2 = yr - (y-1)	mit yr eingesetzt:

     Fehler2 = r - x - y + 2y - 1

 Ist Fehler1 kleiner als Fehler2,  so bleibt man bei y,  sonst geht man einen
 y Wert runter.

 Diese beiden Fehler kann man auch zusammenfassen in einen Gesamtfehler. Dazu
 mu man wissen, da man 2 Werte auch vergleichen kann, indem man sie vonein-
 ander abzieht. Kommt 0 raus, so sind sie gleich. Kommt ein grerer Wert als
 0 raus,  so ist der erste Fehler grer als der zweite.  Kommt aber ein Wert
 kleiner als 0 raus, so ist der zweite Fehler grer als der erste.

   Fehler = Fehler1 - Fehler2

 Fehler ist jetzt der Gesamtfehler.

 Ist Fehler < 0, so ist der zweite Fehler grer,  das heit wir machen einen
 greren Fehler,  wenn wir einen Wert runter gehen,  also bleiben wir besser
 bei y.

 Ist umgekehrt Fehler > 0,  so sind wir  besser beraten,  wenn wir einen Wert
 runter gehen.

 Fr Fehler = 0 kann man tun was man will. Ich bleibe hier bei y.

 Rechnen wir mal den Fehler aus:

   Fehler = Fehler1 - Fehler2

   Fehler = (y - r + x) - (r - x - y + 2y - 1)

 also

       Fehler = y - r + x - r + x + y - 2y + 1
   <=> Fehler = 2y - 2y + 2x -2r + 1

 Wenn wir diese Formel in ein Programm implementieren, lassen sich schon ganz
 schne Geschwindigkeiten erreichen, auf alle Flle besser, als wenn die Wur-
 zel gezogen wrde, wie schon mal oben besprochen!  Unser Verfahren wird also
 immer besser!!

 Es wird aber noch besser (versprochen!).

 Gucken wir uns mal an, was fr einen Wert der Fehler fr x=0 annimmt. Es ist
 aber auch bekannt, da bei x=0 der y Wert gleich dem Radius ist.

 Nochmal: Bei x = 0 gilt:

   y = r

 Fehler = 2y - 2y + 2x -2r + 1

 mit y = r, x = 0 eingesetzt:

   Fehler = 2r - 2r + 0 - 2r + 1 = -2r + 1

 Also gilt fr den Fehler bei x = 0:

   Fehler = -2r + 1

 Betrachten wir nochmal den Fehler, den wir fr jedes x berechnen mssen. Ein
 ziemlich komplizierter Ausdruck:

   Fehler = 2y - 2y + 2x -2r + 1

 Der Fehler besteht in Prinzip aus 3 Teilen:

   * Einen konstanten Anteil (alle reine Zahlen und der Radius, der bleibt
     beim gesamten Kreis konstant)

   * Einen Teil, der sich ndern knnte, wenn wir entscheiden den Wert zu
     verringern (alles, was mit y zusammenhngt)

   * Einen Teil, der immer dazukommt, also alles was mit x zusammenhngt.

 Ziel ist es, von x-Wert zu x-Wert nur noch die nderungen des Fehlers zu be-
 rechnen,  um ihn nicht jedesmal neu berechnen zu mssen.  Ziel ist es irgend
 etwas zu finden, was so aus sieht:

   NeuerFehler = AlterFehler + irgendwas

 Es geht um dieses irgendwas.

 Der konstante Anteil ist, wenn wir schon einen Fehler haben, schon drin, man
 braucht ihn also nie mehr aufzuaddieren.

 Der Teil, der von y abhngig ist, der ndert sich nur, wenn das y verringert
 wird, sonst bleibt er ebenfalls konstant. Aber wie ndert er sich?

 Alles was mit y im Fehler zu tun hat lautet:

   2y - 2y

 Es gilt:

   NeuerFehler = AlterFehler + irgendwas

 NUR bezogen auf y gilt also:

   NeuerFehler = AlterFehler + NeuerFehler - AlterFehler

 Zur Erinnerung:

   NeuerFehler = AlterFehler + irgendwas

 ist unser Ziel.

 Rechnet man das aus, so kommt man auf die Gleichung:

   NeuerFehler = NeuerFehler

 Betrachtet man die Gleichung aber genau,  sieht man,  da dieses 'irgendwas'
 ja genau NeuerFehler - AlterFehler sein muss.

 Der neue Fehler  ist ja 2(y-1) - 2(y-1),  jetzt nur bezogen auf alles,  was
 mit y zu tun hat.  Es muss also folgendes zu AlterFehler dazuaddiert werden,
 um auf NeuerFehler zu kommen:

       NeuerFehler = AlterFehler + 2(y-1) - 2(y-1) - [2y - 2y]
   <=> NeuerFehler = AlterFehler + 2(y -2y +1) - 2y + 2 - 2y + 2y
   <=> NeuerFehler = AlterFehler + 2y - 4y + 2 -2y + 2 -2y + 2y
   <=> NeuerFehler = AlterFehler - 4y + 4

 Zum programmtechnischen: Etwas mit 4 zu multiplizieren kann man auch dadurch
 machen, da man die Zahl um 2 Bits nach links shiftet.

 Bleibt nur die nderung, die jedesmal durchgefhrt werden muss. Ziel ist es,
 genau wie eben ein 'irgendwas' zu finden, welches folgendes erfllt:

   NeuerFehler = AlterFehler + irgendwas

 Es gilt:

   NeuerFehler = AlterFehler + NeuerFehler - AlterFehler

 Beim Fehler mu jedesmal 2x berechnet werden.  Bei x+1  ist der Fehler dann
 2(x+1),  jetzt nur bezogen auf den x abhngigen Teil.  Wie man unten sieht,
 heben sich die Quadrate nochmal schn auf!

 Es gilt:

   NeuerFehler = AlterFehler + 2(x+1) - 2x = 2x + 4x + 2 - 2x = 4x + 2

 Jetzt noch mal alles in Ruhe als Pascalprogramm...


 Berechnung eines 1/8 Kreis nach Bresenham:
 ------------------------------------------

 Ich schreibe den Algorithmus mal als Pseudo-Pascalcode auf:

   VAR x, y, r, Fehler: Integer;

   ...

   Fehler := -2*r + 1;

   FOR x := 1 TO r div 2 DO
   BEGIN
     Plotpixel(x,y);
     IF Fehler > 0 THEN
     BEGIN
       y := y - 1;
       Fehler := Fehler - 4*y + 4;
     END;
     Fehler := Fehler + 4*x + 2;
   END;

 Das ist es!! Mehr nicht!

 Um jetzt einen vollen Kreis zu zeichnen,  muss man statt der einen Koordina-
 te zustzlich noch die 7 anderen einsetzen. Zur Erinnerung, alle 8 waren:

   ( +x, +y )
   ( -x, +y )
   ( -y, +x )
   ( -y, -x )
   ( -x, -y )
   ( +x, -y )
   ( +y, -x )
   ( +y, +x )

 Ich bin jetzt einfach  immer von 0,0 ausgegangen.  Um jetzt den Kreismittel-
 punkt woanders als bei (0,0) zu haben, muss man einfach zum x-Wert der Punk-
 te den x-Wert des Kreismittelpunkts addieren,  und zum y Wert den y.Wert des
 Kreismittelpunkts.


 Bemerkungen
 -----------

 Wir haben  eine Methode kennengelernt,  die mit nur einem  Shiftoperator (*4
 ist das gleiche wie 2 mal nach links schieben) und Addition bzw. Subtraktion
 auskommt.

 In der Wirklichkeit ist dieses nicht immer der Fall. Meistens muss man den y
 Wert noch mit einer Korrekturkonstante multiplizieren,  um kein Ei (Ellipse)
 statt eines Kreises zu bekommen.  Diese Korrekturkonstante nennt man Aspekt-
 ratio.

 Diese Aspektratio kann man ausrechnen:

 Ich mache dieses mal fr 320x200; fr 640x480, 800x600 oder 1024x768 braucht
 man dieses z.B. nicht, weil da die Aspektratio = 1 ist (kann man ausrechnen)

 Um die Aspektratio auszurechnen,  mu man wissen, da fast alle Monitore und
 Fernsehgerte im Verhltnis 4:3 gebaut sind,  also das Verhltnis Breite zur
 Hhe mu 4:3 betragen.  Dieses gilt  nicht immer.  In der  Zeitungsindustrie
 werden oft Monitore verwendet,  die ein Verhltnis 3:4 haben,  damit man das
 gesamte DIN A4 Blatt besser sehen kann.  Auerdem wird sich  in den nchsten
 paar Jahren  im Fernsehbereich  die 16:9 Norm  durchsetzen,  vielleicht auch
 bald fr Computermonitore.

 Zurueck zur Berechnung. Man kann folgende Formel aufstellen:

   Breite/Hhe = 4/3 = a * xPixel / yPixel

 a ist dann die Aspektratio:

 Es gilt dann:

   a = 4*yPixel / (3*xPixel)

 Fr 320x200 gilt:

   a = (4*200) / (3*320)
   a = 5/6

 Um also statt einem 'Ei' einen richtigen Kreis zu bekommen,  mu man fr den
 Modus 320x200 seinen errechneten y-Wert mit 5/6 multiplizieren.  Besser ist,
 erst mit 5 zu multiplizieren,  und dann mit 6 zu teilen, als mit Fliekomma-
 zahlen zu rechnen.  Mit y-Wert ist der  reale y-Wert gemeint,  und nicht die
 errechnete Variable y im Bresenham Kreisverfahren.

 Das untenstehende Programm  entspricht direkt  dem oberen,  nur mit ein paar
 Zustzen: Es bercksichtigt die Aspektratio,  einen anderen Kreismittelpunkt
 als 0,0 (Kreismittelpunkt wird durch die Koordinaten (xm,ym) angegeben), und
 es zeichnet einen vollstndigen Kreis.

 y1 und y2 geben die  verschiedenen mglichen y-Werte an -  also einmal x und
 einmal y.  Schaut man sich z.B. diese beiden der 8 Punkte an, dann sieht man
 sofort, was ich meine:

   ( +x, +y )
   ( +y, +x )

 Bercksichtigt man die Aspektratio, so lauten die Koordinaten:

   (+x, +y*5/6)
   (+y, +x*5/6)

 Also mu einmal y und einmal x mit 5/6 multipliziert werden. Damit man nicht
 bei allen 8 Punkten  immer wieder mit 5/6  multiplizieren mu,  habe ich die
 beiden Variablen y1 und y2 eingefhrt.

   VAR xm,ym,r : Integer;
        Fehler : Integer;
         y1,y2 : Integer;

   ...

   Fehler := -2*r + 1;

   FOR x := 1 TO r div 2 DO
   BEGIN

     y1 := (y * 5) div 6;
     y2 := (x * 5) div 6;

     Plotpixel(xm+x,ym+y1);
     Plotpixel(xm-x,ym+y1);
     Plotpixel(xm-y,ym+y2);
     Plotpixel(xm-y,ym-y2);
     Plotpixel(xm-x,ym-y1);
     Plotpixel(xm+x,ym-y1);
     Plotpixel(xm+y,ym-y2);
     Plotpixel(xm+y,ym+y2);

     IF Fehler > 0 THEN
     BEGIN
       y := y - 1;
       Fehler := Fehler - 4*y + 4;
     END;
     Fehler := Fehler + 4*x + 2;
   END;

 Falls es wirklich auf Geschwindigkeit ankommt, so koennte man sogar eine Ta-
 belle fr die verschiedenen Aspektratios anlegen.


 Anwendungen fr den Bresenham Kreisalgorithmus
 ----------------------------------------------

 Den Algorithmus kann man verwenden um:

   * Einen Kreis zu zeichnen

   * Eine Ellipse zu zeichnen (hier absichtlich eine falsche Aspektratio
     einsetzen)

   * Gefllte Kreise (statt Punkte einfach Verbindungslinien zeichnen). Evtl.
     hilft es, vorher ein mglichst groes Quadrat (bzw. Rechteck) schon vor-
     zuzeichnen.

   * Ein 'Kugel' zu zeichnen. Hierzu einfach von 0 bis r Kreise zeichnen, je-
     weils in einer anderen Farbabstufung.

   * Einen Sinusscroller zu emulieren  (einen positiven Halbkreis gefolgt von
     einem negativen, dann einen Positiven, etc...).

     In einer Demo  drfte AFAIK  niemand den Unterschied zu einer echten Si-
     nuskurve merken knnen (weil es im Prinzip eine ist).  Um die 'Frequenz'
     zu ndern,  einfach den x-Wert  mit einem Wert multiplizieren (am besten
     als Bruch,  so geht es  meist schneller),  um die 'Amplitude' zu ndern,
     einfach  den  realen y-Wert  mit einem  anderen Wert  multiplizieren (im
     Prinzip also die Aspektratio erhhen).

 Auf den letzen Punkt hatte ich die Idee erst gerade vor ca. 5 Minuten. Viel-
 leicht  ist das die Methode,  die schon berall  verwendet wird,  und ich es
 erst jetzt begriffen habe! :-)

 Bresenham Lines kann man  nach einer ganz hnlichen Methode zeichnen lassen,
 die Herleitungsmethode  ist die gleiche.  Vielleicht bespreche ich das Topic
 in einer zuknftigen Version dieser FAQ.



 === ALG0019 ================================================================

 Random (Zufallsverfahren)

 Horst Kraemer 2:2410/249.5



 === ALG0020 ================================================================

 CRC (Prfsummenbildung)

 === ALG0021 ================================================================

 CRC32 (Prfsummenbildung)

 === FRM0001 ================================================================

 PCX-Format (Grafik)

 Andreas Brodmann 2:301/406.19:



 === FRM0002 ================================================================

 MOD-Format (Musik)

 === FRM0003 ================================================================

 FLI-Format (Animation)

 Andreas Brodmann 2:301/406.19:



 === FRM0004 ================================================================

 FLC-Format (Animation)

 Andreas Brodmann 2:301/406.19:



 === LSG0001 ================================================================

 Wie erfahre ich, welche CPU vorhanden ist?

 Juergen Thelen 2:2450/645.5:

 Es existieren verschiedene Wege, um eine CPU zu identifizieren. Der imo wohl
 exakteste Weg ist die sogenannte "CPU-Shutdown Methode".  Bei dieser Methode
 wird meist ein "Triple Fault" ausgelst, wodurch die CPU in den ShutdownMode
 geht und im Real Address Mode erneut gestartet wird. Beim Neustart enthalten
 dann verschiedene Register Codes, anhand derer sich Hersteller, Prozessoren-
 modell und -typ, Masken, Revisionen u.. identifizieren lassen.

 Allerdings ist diese Methode zum einen sehr aufwendig,  und zum anderen sind
 derart przise Identifizierungen nur sehr selten erforderlich.

 Eine weiterer Weg  ist die sogenannte  "Illegal Opcode Methode".  Dabei wird
 eine  Interrupt 6 ISR eingeklinkt,  die auf die Ausfhrung von dem Prozessor
 unbekannten Opcodes reagiert.  Die Identifikationsroutine fhrt dann einfach
 nacheinander  Instructions aus,  die erst ab einer bestimmten CPU-Reihe exi-
 stieren. Wird Interrupt 6 aufgrund eines unbekannten Opcodes ausgelst, kann
 dann ermittelt werden,  welche Instruction die letzte noch bekannte war, und
 folglich ist die CPU identifiziert.

 Da auch diese Methode  nicht ganz problemlos ist,  setze ich meist die nach-
 folgende Routine ein,  die grtenteils  auf offiziellen Intel-Informationen
 beruht. Voraussetzung  fr den Einsatz der Routine  ist allerdings, da sich
 die CPU im Real Address Mode befindet (Grnde:  offizieller Intel 286-Check,
 sowie Beschreiben des Code Segmentes).

 Btw: Das ist eine etwas ltere Version des Moduls und noch nicht vollstndig
      redundanzfrei. Komme im Moment nicht an meine aktuelle Version,  da ich
      mir beim Testen  eines meiner Proggies  mal wieder eine der Lib-Platten
      geschossen hab' und erstmal die FAT restaurieren muss... ;)


   ;--- Snip ----------------------------------------------------------------
   ;
   ; Modul     Get_CPU_ID_
   ; Sprache   TASM 3.2
   ; Autor     Juergen Thelen 2:2450/645.5
   ;
   ; Aufgabe   Identifikation der Central Processing Unit
   ;
   ; In        -
   ;
   ; Out       AL = CPUTyp:
   ;
   ;             1   NEC V20
   ;             2   NEC V30
   ;             3   8086
   ;             4   8088
   ;             5   80186
   ;             6   80188
   ;             7   80286
   ;             8   80386
   ;             9   80486
   ;            10   P5
   ;            11   PPro (kann das jemand besttigen? Hab' nur P5...)
   ;            12   Sexium? ;)
   ;
   ; Chg       EAX, BX, ECX, EDX, SI, DI, ES


   PROC Get_CPU_ID_

     ;------
     ; Prfung auf 8086, 8088, NEC V20, NEC V30, 80186, 80188
     ;
     ; Auf CPUs dieser Serien sind die Bits 12..15 in FLAGS immer gesetzt.

     pushf
     pop    ax                             ; AX = FLAGS
     and    ax, 0fffH                      ; Bits 12..15 lschen
     push   ax
     popf                                  ; FLAGS forcieren
     pushf
     pop    ax                             ; FLAGS erneut holen
     and    ax, 0f000H                     ; Bits 12..15 isolieren
     cmp    ax, 0f000H                     ; Bits wieder gesetzt?
     jne    @@5                            ; nein, 286+ ->

     ;------
     ; Prfung auf 80186, 80188
     ;
     ; CPUs dieser Serien ignorieren Shift-Anweisungen > 32bit, d.h. sie
     ; shiften berhaupt nicht. 8086/8088/V20/V30 dagegen shiften 32bit.

     mov    ax, 0ffffH
     mov    cl, 33
     shl    ax, cl                         ; 80186/80188?
     je     @@1                            ; nein, 8086/8088/V20/V30 ->

     ;------
     ; Unterscheidung zwischen 80186 und 80188
     ;
     ; Erfolgt durch Prfung der Befehlsqueue-Lnge.

     mov    dl, 6                          ; angenommen 80188
     jmp    @@3

   @@1:

     ;------
     ; Prfung auf NEC V20, NEC V30
     ;
     ; CPUs dieser Serien arbeiten im Gegensatz zu 8086/8088 auch einen
     ; Segment Override bei REP Stringbefehlen korrekt ab. Ein 8086/8088
     ; wuerde aufgrund seines "Multiple Prefix Bugs" mit CX = fffeH enden.

     xor    si, si
     mov    cx, 0ffffH
     sti
     rep    es:lodsb
     or     cx, cx                         ; komplett abgearbeitet worden?
     jne    @@2                            ; nein, dann 8086/8088 ->

     ;------
     ; Unterscheidung zwischen NEC V20 und NEC V30
     ;
     ; Erfolgt durch Prfung der Befehlsqueue-Lnge.

     mov    dl, 2                          ; angenommen V30
     jmp    @@3

     ;------
     ; Unterscheidung zwischen 8086 und 8088
     ;
     ; Erfolgt durch Prfung der Befehlsqueue-Lnge.

   @@2:
     mov    dl, 4                          ; angenommen 8088
     jmp    @@3

   @@3:

     ;------
     ; Unterscheidung 8bit, 16bit Datenbus
     ;
     ; CPUs mit 16bit Datenbus (8086/80186/NEC V20) besitzen einen 6 Bytes,
     ; 8088/80188/NEC V30 (8bit) einen nur 4 Bytes langen Befehlsqueue.
     ;
     ; Bei Ausfhrung von REP STOSB wren auf 16bit-Systemen bereits die
     ; Befehle CLD,NOP,NOP,NOP,NOP,DEC DL im Queue, d.h. das DEC DL wrde
     ; ausgefhrt, obwohl es vom REP STOSB genopped wird. Auf 8bit-Systemen
     ; wren nur CLD,NOP,NOP,NOP im Queue, das vierte NOP und DEC DL wrden
     ; also mit effektiver Auswirkung genopped.

     push   cs
     pop    es
     mov    di, Offset @@4
     mov    al, 90H                        ; Opcode fr NOP
     mov    cx, 2
     std
     cli
     rep    stosb
     cld
     nop
     nop
     nop
     nop

   @@4:
     dec    dl                             ; 8086/80186/V20 OHNE Wirkung
     sti
     jmp    @@X

     ;------
     ; Prfung auf 80286
     ;
     ; Auf CPUs dieser Serie sind im RealMode die Bits 12..15 in FLAGS
     ; immer gelscht.
     ;
     ; Diese Prfung NUR im RealMode ausfhren, sonst Hang-a-la-bang... ;)

   @@5:
     pushf
     pop    ax                             ; AX = FLAGS
     or     ax, 0f000H                     ; Bits 12..15 setzen
     push   ax
     popf                                  ; FLAGS forcieren
     pushf
     pop    ax                             ; FLAGS erneut holen
     and    ax, 0f000H                     ; Bits wieder gelscht?
     jne    @@6                            ; nein, 386+ ->

     mov    dl, 7                          ; CPU ist 80286
     jmp    @@X

     ;------
     ; Prfung auf 80386
     ;
     ; Auf CPUs dieser Serie ist Bit 18 (AC-Bit) in EFLAGS immer gelscht.

   @@6:
     mov    bx, sp
     and    sp, 0fffcH                     ; alignen um AC-Fault zu vermeiden
     pushfd
     pop    eax
     mov    ecx, eax
     xor    eax, 00040000H                 ; = Bit 18 flip
     push   eax
     popfd
     pushfd
     pop    eax
     xor    eax, ecx                       ; Bit 18 gendert?
     mov    sp, bx
     jne    @@7                            ; ja, 486+ ->

     mov    dl, 8                          ; CPU ist 80386
     jmp    @@X

   @@7:
     and    sp, 0fffcH                     ; alignen um AC-Fault zu vermeiden
     push   ecx
     popfd
     mov    sp, bx

     ;------
     ; Prfung auf 80486
     ;
     ; Auf CPUs dieser Serie ist Bit 21 (CPUID-Bit) in EFLAGS normalerweise
     ; gelscht (bis auf einige neuere 486er)

     mov    dl, 9                          ; 80486 angenommen

     pushfd
     pop    eax
     mov    ecx, eax
     xor    eax, 00200000H                 ; = Bit 21 flip
     push   eax
     popfd
     pushfd
     pop    eax
     xor    eax, ecx                       ; Bit 21 gendert?
     je     @@X                            ; nein, dann ist es ein 80486 ->

     push   ecx
     popfd

     mov    eax, 1
     db     0fH, 0a2H                      ; Opcodes fr CPUID
     shr    al, 4                          ; Model auf Basis
     and    al, 0fH                        ; und isolieren
     mov    dl, 5                          ; Minimum 486
     add    dl, al                         ; = 9 (486), = 10 (P5) ...

   @@X:
     mov    al, dl
     ret

   ENDP Get_CPU_ID_

   ;--- Snap ----------------------------------------------------------------



 === LSG0002 ================================================================

 Wie erfahre ich, welche FPU vorhanden ist?

 Juergen Thelen 2:2450/645.5:

 Nachstehend meine Routine zur Identifizierung einer FPU.  Das Modul bentigt
 zur FPU-Identifizierung den von Get_CPU_ID_ ermittelten CPU-Typ (LSG0001).

 Diese Routine entspricht  weitgehend der "offiziellen" Intel-Methode und ist
 fr den Real Mode konzipiert (Beschreiben des Code Segments).


   ;--- Snip ----------------------------------------------------------------
   ;
   ; Modul     Get_FPU_ID_
   ; Sprache   TASM 3.2
   ; Autor     Juergen Thelen 2:2450/645.5
   ;
   ; Aufgabe   Identifikation der Floating Point Unit
   ;
   ; In        AL = CPUTyp:
   ;
   ;             1   NEC V20
   ;             2   NEC V30
   ;             3   8086
   ;             4   8088
   ;             5   80186
   ;             6   80188
   ;             7   80286
   ;             8   80386
   ;             9   80486
   ;            10   P5
   ;
   ; Out       AL = FPUTyp:
   ;
   ;             0   keine
   ;             1   8087
   ;             2   80187
   ;             3   80287
   ;             4   80387
   ;             5   integriert
   ;
   ; Chg       AX, BL, SI

   PROC Get_FPU_ID_

     mov    bl, al                         ; CPUTyp
     push   ds

     call   @@1

     DW     0                              ; FPU Status/Control Word

   @@1:

     ;------
     ; Zur FPU-Identifizierung wird festgestellt, ob sich Status und Control
     ; Words des Coprozessors beschreiben lassen.
     ;
     ; Funktioniert dies nicht, ist logischerweise keine FPU vorhanden.
     ;
     ; Ist eine FPU vorhanden, wird anhand des zuvor ermittelten CPU-Typs der
     ; der entsprechende FPU-Typ ermittelt.
     ;
     ; Lediglich bei einem 80386 (CPUTyp 8) erfolgt eine weitere Prfung, da
     ; auf 80386ern eine 80287 oder eine 80387 FPU betrieben werden kann.
     ; Zur Unterscheidung wird die Endlichkeit der FPU berprft, denn nur
     ; auf 80287 FPUs ist die positive Endlichkeit gleich der negativen.

     pop    si                             ; Offset fr Status/Control Word
     push   cs
     pop    ds

     fninit                                ; FPU Status Reset
     mov    [w si], 5a5aH                  ; auf Wert ungleich Null setzen
     fnstsw [si]                           ; Status Word sichern
     mov    ax, [si]
     or     al, al                         ; ist Status jetzt Null?
     je     @@3                            ; ja, knnte FPU sein ->

   @@2:
     xor    al, al                         ; definitiv keine FPU
     jmp    @@X

   @@3:
     fnstcw [si]                           ; Control Word auf Null setzen
     mov    ax, [si]
     and    ax, 103fH
     cmp    ax, 3fH                        ; Nullen und Einsen gelesen?
     jne    @@2                            ; nein, keine FPU ->

     mov    al, 1                          ; FPU existiert
     cmp    bl, 8                          ; CPUTyp = 80386?
     je     @@4                            ; ja, Infinity prfen ->

     :------
     ; FPU ist 8087, 80187, 80287 oder integriert

     mov    al, 2                          ; 8087 angenommen
     cmp    bl, 5                          ; entsprechende CPU?
     jb     @@X                            ; ja ->

     mov    al, 3                          ; 80187 angenommen
     cmp    bl, 7                          ; entsprechende CPU?
     jb     @@X                            ; ja ->

     mov    al, 4                          ; 80287 angenommen
     cmp    bl, 7                          ; entsprechende CPU?
     je     @@X                            ; ja ->

     mov    al, 5                          ; integrierte FPU (486+)
     jmp    @@X

   @@4:
     fld1                                  ; Standard Control von FNINIT
     fldz                                  ; Endlichkeit generieren
     fdiv
     fld    st                             ; negative Endlichkeit
     fchs                                  ; bei 80387 pos <> neg
     fcompp                                ; vergleichen und entfernen
     fstsw  [si]                           ; Status von FCOMPP sichern
     mov    ax, [si]
     mov    al, 3                          ; 80287 angenommen
     sahf                                  ; Endlichkeiten gleich?
     je     @@X                            ; ja ->

     mov    al, 4                          ; nein, ist 80387

   @@X:
     pop    ds

   ;--- Snap ----------------------------------------------------------------



 === LSG0003 ================================================================

 Wo liegen die Kommandozeilenparameter meines Programmes?

 Juergen Thelen 2:2450/645.5:

 Die Kommandozeilenparameter  findet man  im PSP (Program Segment Prefix) des
 Programmes. Die Adresse des PSP erfhrt man ber Int 21.62H (BX = PSP).

 Die Parameter  des Programmes plus CR (0dH)  liegen ab PSP:0081H.  Die Lnge
 der Kommandozeile (ohne CR) liegt bei PSP:0080H (Byte).



 === LSG0004 ================================================================

 Wie komme ich an das Startverzeichnis meines Programmes?

 Juergen Thelen 2:2450/645.5:

 Den vollstndigen Pfad  zu seinem Programm  findet man  seit DOS 3.0+ hinter
 den DOS-Variablen im Environment Segment seines Prozesses.

 Nachfolgend eine meiner  allgemeineren Routinen,  die unter anderem auch zur
 Ermittlung der RunPath-Adresse dient:


   ;--- Snip ----------------------------------------------------------------
   ;
   ; Modul     Get_Env_Parms_
   ; Sprache   TASM 3.2
   ; Autor     Juergen Thelen 2:2450/645.5
   ;
   ; Aufgabe   Ermitteln von Adresse und Lnge des Environments, sowie des
   ;           RunPath-Offsets.
   ;
   ; In        -
   ;
   ; Out       AX = Environment-Segment des aktuellen Prozesses
   ;
   ;           BX = Offset des ersten RunPath-Zeichens (ASCIZ)
   ;                BX-2 = Lnge des Environments EXKL. RunPath (nur EnvVars)
   ;
   ;           CX = Gesamtlnge des Environments INKL. RunPath
   ;
   ; Chg       AX, BX, CX

   PROC Get_Env_Parms_

     STRUC frm
       pDS      DW ?
       pDI      DW ?
       pSI      DW ?
       pBP      DW ?
       pSP      DW ?
       pBX      DW ?
       pDX      DW ?
       pCX      DW ?
       pAX      DW ?
     ENDS frm

     pusha
     push   ds
     mov    bp, sp                         ; Stackframe initialisieren

     mov    ah, 62H                        ; GET_CURRENT_PID
     int    21H                            ; PSP des aktiven Prozesses holen

     mov    ds, bx
     mov    ax, [ds:002cH]                 ; EnvSeg des aktiven Prozesses
     mov    [bp+frm.pAX], ax               ; sichern
     mov    ds, ax
     xor    si, si                         ; DS:SI = EnvSeg:0
     xor    cx, cx
     cld

   @@1:
     lodsb
     inc    cl
     or     al, al                         ; Ende einer Variable ?
     jne    @@1                            ; nein ->

     cmp    [b si], 0                      ; Ende aller Variablen ?
     jne    @@1                            ; nein ->

     add    cx, 3
     add    si, 3                          ; Zeiger auf Start des RunPaths
     mov    [bp+frm.pBX], si               ; sichern

   @@2:
     lodsb
     inc    cl
     or     al, al                         ; Ende des RunPaths ?
     jne    @@2                            ; nein ->

     mov    [bp+frm.pCX], cx               ; = Gesamtlnge Environmentblock

     pop    ds
     popa
     ret

   ENDP Get_Env_Parms_

   ;--- Snap ----------------------------------------------------------------



 === LSG0005 ================================================================

 Grundgerst fr RM/Flat4G und XMS

 Juergen Thelen 2:2450/645.5:

 Hier eine meiner Beispielsources fr den Pseudomodus RM/Flat4G (s. BAW0015).

 Sie zeigt, wie man unter RM/Flat4G mit XMS zusammen arbeiten kann.  Die XMS-
 Untersttzung kann  in mehreren Fllen durchaus sinnvoll sein,  z.B. fr den
 Fall,  da der Anwender das DOS-Kernel oder sonstige Dinge in die HMA ausge-
 lagert hat, um konventionelles RAM zu sparen, oder fr den Fall, da man auf
 die XMS-Fhigkeit, Speicherbereiche gegen Verlagerung durch andere Programme
 sperren zu knnen (Konfliktverhinderung), nicht verzichten mchte, o..

 Normalerweise sind Datenbewegungen (Copy o..) in XMS-Blocks bekanntlich nur
 ber den Treiber, genauer ber das Handle des entsprechenden Blocks mglich.
 Nach dem LOCKen eines oder mehrerer XMS-Blocks  gilt das unter RM/Flat4G na-
 trlich nicht mehr... &)

 Wer es lieber RAW mag ;), oder nach Mglichkeiten zur direkten A20-Steuerung
 oder einer WrapAround-Prfung sucht, findet in LSG0006 eine entsprechend ab-
 genderte Source-Variante...


 ;--- Snip ------------------------------------------------------------------
 ;
 ; Programm  RMF4GXMS
 ; Sprache   TASM 3.2
 ; Autor     Juergen Thelen 2:2450/645.5
 ;
 ; OS        DOS 2.0+
 ; CPU       386+
 ;
 ; Aufgabe   Grundgerst RM/Flat4G fr XMS-Umgebungen
 ;
 ; .OBJ      TASM /m /ml /dMDL=Modell RMF4GXMS.ASM
 ; .EXE      TLINK /c RMF4GXMS.OBJ
 ;

 IFNDEF MDL

   DISPLAY "Fehler: '/dMDL=Modell' zur Assemblierung erforderlich!"
   ERR

 ELSE

 % MODEL USE16 MDL

 IDEAL
 JUMPS
 LOCALS
 DOSSEG

 STACK

 ;---------------------------------------------------------------------------

 o EQU Offset
 s EQU Seg
 b EQU byte ptr
 w EQU word ptr
 d EQU dword ptr
 p EQU pword ptr

 ;---------------------------------------------------------------------------

 ; Nachfolgende Equates einfach den jeweiligen Anforderungen anpassen... ;)

 MIN_XMS =   4096    ; Min. KByte XMS-RAM (frei)
 MIN_CNV =    256    ; Min. KByte konventionelles RAM (frei)

 ;---------------------------------------------------------------------------

 P8086

 CODESEG
   STARTUPCODE

   ;------
   ; berflssigen Heap an DOS zurckgeben

   mov    bx, sp
   add    bx, 15
   mov    cl, 4
   shr    bx, cl
   mov    ax, ss
   add    bx, ax
   mov    ax, es
   sub    bx, ax
   mov    ah, 4aH                       ; ResizeMemBlock
   int    21H                           ; Heap an DOS zurckgeben

   mov    dx, o DTitle
   mov    ah, 9
   int    21H

   call   Init_                         ; Fehler whrend Initialisierung?
   jc     Cancel                        ; ja ->

   ;------
   ; Prozessor fr RM/Flat4G einrichten

   P386

   mov    eax, s RM4G_GDT
   shl    eax, 4                        ; GDT-Segment Paragraph -> Linear
   xor    ebx, ebx
   mov    bx, o RM4G_GDT
   add    eax, ebx                      ; + Offset(GDT)
   mov    [d RM4G_GDTR+2], eax          ; = GDTR.Base
   mov    [w RM4G_GDTR], 2*8            ; GDTR.Limit = 2 Descriptors
   lgdt   [p RM4G_GDTR]                 ; GDTR fr neuen GDT laden

   push   ds                            ; Bezug zum DATASEG sichern
   cli

     mov    eax, cr0
     or     eax,1                       ; CR0.PE setzen
     mov    cr0, eax                    ; rein in den Protected Mode
     jmp    @@PQFlush1                  ; Prefetch-Queue lschen!

 @@PQFlush1:
     mov    bx, 8                       ; Selector fr 2. Descriptor

     mov    ds, bx                      ; DS.Limit = 4 GByte
     mov    es, bx                      ; ES.Limit = 4 GByte
     mov    fs, bx                      ; FS.Limit = 4 GByte
     mov    gs, bx                      ; GS.Limit = 4 GByte

     and    eax, 0feH                   ; CR0.PE lschen
     mov    cr0, eax                    ; zurck in den Real Address Mode
     jmp    @@PQFlush2                  ; Prefetch-Queue lschen!

 @@PQFlush2:
   sti
   pop    ds                            ; Bezug zum DATASEG restaurieren

   call   Main_                         ; Hauptprogramm
   call   CleanUp_                      ; aufrumen

   xor    al, al                        ; ExitCode: ok
   jmp    EndProgram

 Cancel:
   mov    bx, dx
   shl    bx, 1
   add    bx, o ErrTxtTbl
   mov    dx, [bx]
   mov    ah, 9
   int    21H

   mov    al, 1                         ; ExitCode: Fehler

 EndProgram:
   mov    ah, 4cH
   int    21H                           ; Programm beenden

 ;--- Init ------------------------------------------------------------------

 PROC Init_

   P8086

   cld

   ;------
   ; Mit Sicherheit scheiterndes AllocMemBlock ausfhren, um den
   ; grten freien Block (= freies konventionelles RAM) nach BX
   ; zu holen.

   mov    bx, 0ffffH                    ; 65.535 Paras
   mov    ah, 48H                       ; AllocMemBlock
   int    21H                           ; = MaxAvail nach BX holen

   ;------
   ; Prfen, ob Anforderung MIN_CNV erfllt ist

   mov    dx, 1                         ; Annahme: Fehlercode 1
   mov    cl, 6
   shr    bx, cl                        ; Paras in KByte
   cmp    bx, MIN_CNV                   ; genug konventionelles RAM frei?
   jb     InitErr                       ; nein, Fehler ->

   ;------
   ; 8086/8088 CPUs ausfiltern

   mov    dx, 2                         ; Annahme: Fehlercode 2
   push   sp
   pop    ax
   cmp    ax, sp                        ; ungleich: 8086/88?
   jne    InitErr                       ; ja, Fehler: kein 386er ->

   P286

   ;------
   ; CPUs im PM bzw. VM ausfiltern (dadurch auch EMS/XMS auf PM-Basis)

   mov    dx, 3                         ; Annahme: Fehlercode 3
   smsw   ax                            ; MSW holen
   shr    ax, 1                         ; MSW.PE ins CF schieben
   jc     InitErr                       ; Fehler: nicht im RM ->

   ;------
   ; 80286 CPUs ausfiltern

   mov    dx, 2                         ; Annahme: Fehlercode 2
   pushf                                ; FLAGS sichern
     pushf
     pop    ax                          ; AX = FLAGS
     or     ax, 0f000H                  ; Bits 12..15 setzen
     push   ax
     popf                               ; FLAGS forcieren
     pushf
     pop    ax                          ; FLAGS erneut holen
   popf                                 ; FLAGS restaurieren
   and    ax, 0f000H                    ; Bits 12..15 wieder gelscht?
   je     InitErr                       ; ja, Fehler: kein 386er ->

   P386

   ;------
   ; Systeme ohne aktiven XMS-Treiber ausfiltern

   mov    dx, 4                         ; Annahme: Fehlercode 4
   mov    ax, 4300H
   int    2fH
   cmp    al, 80H                       ; XMS-Treiber aktiv?
   jne    InitErr                       ; nein, Fehler ->

   ;------
   ; XMS-Vektor fr zuknftige FAR CALLs ermitteln und sichern

   mov    ax, 4310H
   int    2fH
   mov    [w XMSVector], bx
   mov    [w XMSVector+2], es

   ;------
   ; XMS-Versionen vor 2.0 ausfiltern

   xor    ah, ah                        ; GetXMSVersion
   call   [d XMSVector]
   mov    dx, 5                         ; Annahme: Fehlercode 5
   cmp    ah, 2                         ; mindestens 2.x?
   jb     InitErr                       ; nein, Fehler: lter als 2.0 ->

   ;------
   ; Prfen, ob Anforderung MIN_XMS erfllt ist

   mov    ah, 8                         ; QueryFreeXMS
   xor    bl, bl                        ; fr richtigen ErrCode in DirtyBIOSs
   call   [d XMSVector]
   cmp    dx, MIN_XMS                   ; genug XMS-RAM frei?
   mov    dx, 6                         ; Annahme: Fehlercode 6
   jb     InitErr                       ; nein, Fehler ->

   ;------
   ; XMS-Block in Gre MIN_XMS allokieren

   mov    dx, MIN_XMS
   mov    ah, 9                         ; AllocXMSBlock
   call   [d XMSVector]
   mov    [XMSHandle], dx
   mov    dx, 7                         ; Annahme: Fehlercode 7
   or     al, al                        ; erfolgreich (1)?
   je     InitErr                       ; nein, Fehler ->

   ;------
   ; XMS-Block LOCKen (um Verlagerungen auszuschlieen)

   mov    dx, [XMSHandle]
   mov    ah, 12                        ; LockXMSBlock
   call   [d XMSVector]
   mov    [w LFlatBottom], bx           ; Startadresse des
   mov    [w LFlatBottom+2], dx         ; Flat-RAMs (32bit linear)
   mov    dx, 8                         ; Annahme: Fehlercode 8
   or     al, al                        ; erfolgreich (1)?
   je     InitErr                       ; nein, Fehler ->

   ;------
   ; LocalA20Enable (Zugriff auf XMM ermglichen (WrapAround deaktivieren))

   mov    ah, 5                         ; LocalA20Enable
   call   [d XMSVector]
   mov    dx, 9                         ; Annahme: Fehlercode 9
   or     al, al                        ; erfolgreich (1)?
   je     InitErr                       ; nein, Fehler ->

   clc
   jmp    InitX

 InitErr:
   stc

 InitX:
   ret

 ENDP Init_

 ;--- Main ------------------------------------------------------------------

 PROC Main_

   ;
   ; Hier ist der Platz um eure RM/Flat4G (XMS) Schlachten zu schlagen... ;)
   ;
   ; Beispielzugriffe RM/Flat4G:
   ;
   ;   mov    edi, 000b8000H
   ;   mov    al, 7
   ;   mov    [es:edi], al
   ;
   ;   mov    esi, [LFlatBottom]
   ;   mov    al, [es:esi]
   ;   mov    ax, [fs:esi]
   ;   mov    eax, [gs:esi]
   ;
   ; Achso, und nicht vergessen:  Bei einem USE16-Segment  (wie hier)  werden
   ; String-Instructions vom Assembler natrlich 16bit bersetzt (SI/DI).. ;)
   ;

   ret
 ENDP Main_

 ;--- CleanUp ---------------------------------------------------------------

 PROC CleanUp_

   ;------
   ; LocalA20Disable (um nachfolgende Programme nicht zu verwirren ;)

   mov    ah, 6                         ; LocalA20Disable
   call   [d XMSVector]

   ;------
   ; Unseren geLOCKten XMS-Block wieder fr Verlagerungen freigeben

   mov    ah, 13                        ; UnlockXMSBlock
   mov    dx, [XMSHandle]
   call   [d XMSVector]

   ;------
   ; Unseren XMS-Block wieder der Allgemeinheit anvertrauen ;)

   mov    ah, 10                        ; FreeXMSBlock
   mov    dx, [XMSHandle]
   call   [d XMSVector]

   ;
   ; weitere eventuell erforderliche Aufrumarbeiten
   ;

   ret
 ENDP CleanUp_

 ;---------------------------------------------------------------------------

 DATASEG

   RM4G_GDT     db 8 DUP(0)             ; Null-Descriptor

                ; 2. Descriptor mit 4 GByte-Limit

                dw 0ffffH               ; Limit (Bits 0..15)

                dw 0                    ; Base (Bits 0..15)

                db 0                    ; Base (Bits 16..23)

                db 10010010b
              ;     not accessed
              ;      writable
              ;      no ExpandDown
              ;      immer 0
              ;      immer 1
              ;      DPL = 0
              ;     present

                db 11001111b
              ;    Ĵ
              ;        Limit (Bits 16..19)
              ;     available
              ;     immer 0
              ;     big Segment
              ;     Granularity = 4 KByte (Limit in 4 KByte-Units)

                db 0                    ; Base (Bits 24..31)

   RM4G_GDTR    db 6 DUP(0)

   XMSVector    dd 0
   XMSHandle    dw 0

   LFlatBottom  dd 0

   DTitle       db 13, 10
                db 'RM/Flat4G (XMS), (p) 1996 J. Thelen, Public Domain'
                db 13, 10, 36

   ErrTxt1      db 13, 10, 7
                db 'Fehler: 256 KByte RAM erforderlich (nach HeapRelease).'
                db 13, 10, 36

   ErrTxt2      db 13, 10, 7
                db 'Fehler: 80386 CPU (oder hher) erforderlich.'
                db 13, 10, 36

   ErrTxt3      db 13, 10, 7
                db 'Fehler: CPU befindet sich nicht im Real Address Mode.'
                db 13, 10, 36

   ErrTxt4      db 13, 10, 7
                db 'Fehler: XMS-Treiber erforderlich. Bitte aktivieren.'
                db 13, 10, 36

   ErrTxt5      db 13, 10, 7
                db 'Fehler: XMS-Treiber zu alt (min. XMS 2.0+ erforderlich).'
                db 13, 10, 36

   ErrTxt6      db 13, 10, 7
                db 'Fehler: Zuwenig XMS-RAM frei (min. 4 MByte erforderlich).'
                db 13, 10, 36

   ErrTxt7      db 13, 10, 7
                db 'Fehler: XMS-Blockreservierung milungen (FATAL!).'
                db 13, 10, 36

   ErrTxt8      db 13, 10, 7
                db 'Fehler: XMS-Blocksperrung milungen (FATAL!).'
                db 13, 10, 36

   ErrTxt9      db 13, 10, 7
                db 'Fehler: A20-Gate nicht ansteuerbar (FATAL!).'
                db 13, 10, 36

   ErrTxtTbl    dw 0
                dw o ErrTxt1, o ErrTxt2, o ErrTxt3, o ErrTxt4
                dw o ErrTxt5, o ErrTxt6, o ErrTxt7, o ErrTxt8
                dw o ErrTxt9

 ;---------------------------------------------------------------------------

 ENDIF

 END

 ;--- Snap ------------------------------------------------------------------



 === LSG0006 ================================================================

 Grundgerst fr RM/Flat4G und RAW

 Juergen Thelen 2:2450/645.5:

 Hier eine weitere Beispielsource fr RM/Flat4G, die aber im Gegensatz zu der
 vorstehenden LSG0005 nicht mit XMS arbeitet, sondern davon ausgeht, da noch
 kein XMS Memory Manager aktiv ist.  Die Source geht sogar noch etwas weiter,
 und schliet auch die Aktivitt jeglichen EMS-Treibers aus.  Das Fehlen jeg-
 lichen Memory Managers ist brigens auch der Grund, warum man eine derartige
 Betriebsart hufig auch als RAW (englisch, roh) bezeichnet...

 Diese Source ist im Prinzip also fr eigene RM/Flat4G-Programme gedacht, die
 ausschlieen wollen,  da ihnen ein Memory Manager dazwischenfunkt (aus wel-
 chen Grnden auch immer)... ;)

 Genauso gut lieen sich aber auch Teile dieser Source mit LSG0005 verbinden.
 Das wre z.B. sinnvoll, wenn man erreichen mchte, da bei nicht vorhandenem
 XMS-Treiber das Programm halt auf XMS verzichtet und stattdessen einfach RAW
 weiterarbeitet.  Dadurch entfiele fr den Anwender  automatisch der "Zwang",
 erst selbst einen XMS-Treiber aktivieren,  das System resetten  und das Pro-
 gramm erneut starten zu mssen).

 Die Source zeigt auerdem,  wie man die A20-Leitung direkt ber den Keyboard
 Controller steuert und eine WrapAround-Prfung auf die Beine stellt.  Solche
 Routinen sind in LSG0005 nicht enthalten.


 ;--- Snip ------------------------------------------------------------------
 ;
 ; Programm  RMF4GRAW
 ; Sprache   TASM 3.2
 ; Autor     Juergen Thelen 2:2450/645.5
 ;
 ; OS        DOS 2.0+
 ; CPU       386+
 ;
 ; Aufgabe   Grundgerst RM/Flat4G fr RAW-Umgebungen
 ;
 ; .OBJ      TASM /m /ml /dMDL=Modell RMF4GRAW.ASM
 ; .EXE      TLINK /c RMF4GRAW.OBJ
 ;

 IFNDEF MDL

   DISPLAY "Fehler: '/dMDL=Modell' zur Assemblierung erforderlich!"
   ERR

 ELSE

 % MODEL USE16 MDL

 IDEAL
 NOJUMPS
 LOCALS
 DOSSEG

 STACK

 ;---------------------------------------------------------------------------

 o EQU Offset
 s EQU Seg
 b EQU byte ptr
 w EQU word ptr
 d EQU dword ptr
 p EQU pword ptr

 ;---------------------------------------------------------------------------

 ; Nachfolgende Equates knnt ihr natrlich euren Anforderungen entsprechend
 ; anpassen.  In der hier  vorliegenden Form  erwartet das Proggie 550 KByte
 ; freies konventionelles RAM (nach HeapRelease!)...
 ;
 ; Was das frs Debugging bedeuten wrde, drfte wohl klar sein... &)

 MIN_XMM =  16384    ; Min. KByte erweiterter Speicher (installiert)
 MIN_CNV =    550    ; Min. KByte konventionelles RAM (frei)

 ;---------------------------------------------------------------------------

 P8086

 CODESEG
   STARTUPCODE

   ;------
   ; berflssigen Heap an DOS zurckgeben

   mov    bx, sp
   add    bx, 15
   mov    cl, 4
   shr    bx, cl
   mov    ax, ss
   add    bx, ax
   mov    ax, es
   sub    bx, ax
   mov    ah, 4aH                       ; ResizeMemBlock
   int    21H                           ; Heap an DOS zurckgeben

   mov    dx, o DTitle
   mov    ah, 9
   int    21H

   call   Init_                         ; Fehler whrend Initialisierung?
   jc     Cancel                        ; ja ->

   ;------
   ; Prozessor fr RM/Flat4G einrichten

   P386

   mov    eax, s RM4G_GDT
   shl    eax, 4                        ; GDT-Segment Paragraph -> Linear
   xor    ebx, ebx
   mov    bx, o RM4G_GDT
   add    eax, ebx                      ; + Offset(GDT)
   mov    [d RM4G_GDTR+2], eax          ; = GDTR.Base
   mov    [w RM4G_GDTR], 2*8            ; GDTR.Limit = 2 Descriptors
   lgdt   [p RM4G_GDTR]                 ; GDTR fr neuen GDT laden

   push   ds                            ; Bezug zum DATASEG sichern
   cli

     mov    eax, cr0
     or     eax,1                       ; CR0.PE setzen
     mov    cr0, eax                    ; rein in den Protected Mode
     jmp    @@PQFlush1                  ; Prefetch-Queue lschen!

 @@PQFlush1:
     mov    bx, 8                       ; Selector fr 2. Descriptor

     mov    ds, bx                      ; DS.Limit = 4 GByte
     mov    es, bx                      ; ES.Limit = 4 GByte
     mov    fs, bx                      ; FS.Limit = 4 GByte
     mov    gs, bx                      ; GS.Limit = 4 GByte

     and    eax, 0feH                   ; CR0.PE lschen
     mov    cr0, eax                    ; zurck in den Real Address Mode
     jmp    @@PQFlush2                  ; Prefetch-Queue lschen!

 @@PQFlush2:
   sti
   pop    ds                            ; Bezug zum DATASEG restaurieren

   call   Main_                         ; Hauptprogramm
   call   CleanUp_                      ; Fehler whrend des Aufrumens?
   jc     Cancel                        ; ja ->

   xor    al, al                        ; ExitCode: ok
   jmp    EndProgram

 Cancel:
   mov    bx, dx
   shl    bx, 1
   add    bx, o ErrTxtTbl
   mov    dx, [bx]
   mov    ah, 9
   int    21H

   mov    al, 1                         ; ExitCode: Fehler

 EndProgram:
   mov    ah, 4cH
   int    21H                           ; Programm beenden

 ;--- GetWrapState ----------------------------------------------------------

 PROC GetWrapState_
   pusha
   push   ds
   push   es
   les    di, [WrapAddrINT]             ; 0000:0000
   lds    si, [WrapAddrHMA]             ; ffff:0010
   mov    cx, 64/4
   repe   cmpsd                         ; Bereiche vergleichen
   stc                                  ; Annahme: WrapAround ist aktiv
   jcxz   @@X                           ; stimmt ->

   clc                                  ; Annahme falsch, A20 ist frei...

 @@X:
   pop    es
   pop    ds
   popa
   ret
 ENDP GetWrapState_

 ;--- SwitchA20 -------------------------------------------------------------

 PROC SwitchA20_
   pusha
   xor    cx, cx
   mov    dl, 0ddH                      ; Basis A20Disable
   add    dl, al                        ; AL: 0 (Disable), 1 (Enable)
   cli

 @@1:
     in     al, 64H                     ; KBC_STATE
     and    al, 02H                     ; InputBuffer leer?
     loopne @@1                         ; nein ->

     jne    @@4                         ; immer noch voll, TimeOut ->

     mov    al, 0d1H                    ; KBC_DATA Write
     out    64H, al                     ; ankndigen

 @@2:
     in     al, 64H                     ; KBC_STATE
     and    al, 02H                     ; Befehl verarbeitet?
     loopne @@2                         ; nein ->

     jne    @@4                         ; immer noch nicht, TimeOut ->

     mov    al, dl                      ; A20Disable (ddH) | A20Enable (dfH)
     out    60H, al                     ; an KBC_DATA

 @@3:
     in     al, 64H                     ; KBC_STATE
     and    al, 02H                     ; Befehl verarbeitet?
     loopne @@3                         ; nein ->

 @@4:
   sti
   clc                                  ; ok
   je     @@X

   stc                                  ; TimeOut

 @@X:
   popa
   ret
 ENDP SwitchA20_

 ;--- Init ------------------------------------------------------------------

 PROC Init_

   P8086

   cld

   ;------
   ; Mit Sicherheit scheiterndes AllocMemBlock ausfhren, um den
   ; grten freien Block (= freies konventionelles RAM) nach BX
   ; zu holen.

   mov    bx, 0ffffH                    ; 65.535 Paras
   mov    ah, 48H                       ; AllocMemBlock
   int    21H                           ; = MaxAvail nach BX holen

   ;------
   ; Prfen, ob Anforderung MIN_CNV erfllt ist

   mov    dx, 1                         ; Annahme: Fehlercode 1
   mov    cl, 6
   shr    bx, cl                        ; Paras in KByte
   cmp    bx, MIN_CNV                   ; genug konventionelles RAM frei?
   jb     InitErr                       ; nein, Fehler ->

   ;------
   ; 8086/8088 CPUs ausfiltern

   mov    dx, 2                         ; Annahme: Fehlercode 2
   push   sp
   pop    ax
   cmp    ax, sp                        ; ungleich: 8086/88?
   jne    InitErr                       ; ja, Fehler: kein 386er ->

   P286

   ;------
   ; CPUs im PM bzw. VM ausfiltern (dadurch auch EMS/XMS auf PM-Basis)

   mov    dx, 3                         ; Annahme: Fehlercode 3
   smsw   ax                            ; MSW holen
   shr    ax, 1                         ; MSW.PE ins CF schieben
   jc     InitErr                       ; Fehler: nicht im RM ->

   ;------
   ; 80286 CPUs ausfiltern

   mov    dx, 2                         ; Annahme: Fehlercode 2
   pushf                                ; FLAGS sichern
     pushf
     pop    ax                          ; AX = FLAGS
     or     ax, 0f000H                  ; Bits 12..15 setzen
     push   ax
     popf                               ; FLAGS forcieren
     pushf
     pop    ax                          ; FLAGS erneut holen
   popf                                 ; FLAGS restaurieren
   and    ax, 0f000H                    ; Bits 12..15 wieder gelscht?
   je     InitErr                       ; ja, Fehler: kein 386er ->

   P386

   ;------
   ; Prfen, ob Anforderung MIN_XMM erfllt ist

   mov    dx, 4                         ; Annahme: Fehlercode 4
   mov    al, 23
   out    70H, al
   in     al, 71H                       ; Extended Mem Lo holen
   mov    bl, al
   mov    al, 24
   out    70H, al
   in     al, 71H                       ; Extended Mem Hi holen
   mov    bh, al
   cmp    bx, MIN_XMM                   ; genug Extendend Memory da?
   jb     InitErr                       ; nein, Fehler ->

   ;------
   ; Systeme mit aktivem EMS-Treiber (RM-Basis) ausfiltern

   mov    dx, 5                         ; Annahme: Fehlercode 5
   mov    ax, 3567H
   int    21H                           ; ES:BX = Vektor INT 67H
   mov    di, 10
   mov    si, Offset EMS_ID_Str
   mov    cx, 8
   repe   cmpsb                         ; EMS-ID gefunden ('EMMXXXX0')?
   jcxz   InitErr                       ; ja, Fehler: RAW gewnscht ->

   ;------
   ; Systeme mit aktivem XMS-Treiber (RM-Basis) ausfiltern

   mov    dx, 6                         ; Annahme: Fehlercode 6
   mov    ax, 4300H
   int    2fH
   cmp    al, 80H                       ; XMS-Treiber aktiv?
   je     InitErr                       ; ja, Fehler: RAW gewnscht ->

   ;------
   ; Prfen, ob WrapAround aktiv ist, ggf. A20 freischalten

   mov    dx, 7                         ; Annahme: Fehlercode 7
   call   GetWrapState_                 ; WrapAround aktiv?
   jnc    InitX                         ; nein, A20 frei, CF=0 (ok) ->

   mov    al, 1                         ; A20Enable
   call   SwitchA20_                    ; TimeOut?
   jc     InitX                         ; ja, Fehler ->

   call   GetWrapState_                 ; WrapAround immer noch aktiv?
   jnc    InitX                         ; nein, A20 frei, CF=0 (ok) ->

 InitErr:
   stc

 InitX:
   ret

 ENDP Init_

 ;--- Main ------------------------------------------------------------------

 PROC Main_

   ;
   ; Hier knnt ihr jetzt RM/Flat4G (RAW) wten, wie ihr wollt... ;)
   ;

   ret
 ENDP Main_

 ;--- CleanUp ---------------------------------------------------------------

 PROC CleanUp_

   mov    dx, 7                         ; Annahme: Fehlercode 7
   xor    al, al                        ; A20Disable
   call   SwitchA20_                    ; TimeOut?
   jc     @@X                           ; ja, Fehler ->

   ;
   ; weitere eventuell erforderliche Aufrumarbeiten
   ;

 @@X:
   ret
 ENDP CleanUp_

 ;---------------------------------------------------------------------------

 DATASEG

   RM4G_GDT     db 8 DUP(0)             ; Null-Descriptor

                ; 2. Descriptor mit 4 GByte-Limit

                dw 0ffffH               ; Limit (Bits 0..15)

                dw 0                    ; Base (Bits 0..15)

                db 0                    ; Base (Bits 16..23)

                db 10010010b
              ;     not accessed
              ;      writable
              ;      no ExpandDown
              ;      immer 0
              ;      immer 1
              ;      DPL = 0
              ;     present

                db 11001111b
              ;    Ĵ
              ;        Limit (Bits 16..19)
              ;     available
              ;     immer 0
              ;     big Segment
              ;     Granularity = 4 KByte (Limit in 4 KByte-Units)

                db 0                    ; Base (Bits 24..31)

   RM4G_GDTR    db 6 DUP(0)

   WrapAddrINT  dd 0
   WrapAddrHMA  dd 0ffff0010H

   EMS_ID_Str   db 'EMMXXXX0'

   DTitle       db 13, 10
                db 'RM/Flat4G (RAW), (p) 1996 J. Thelen, Public Domain'
                db 13, 10, 36

   ErrTxt1      db 13, 10, 7
                db 'Fehler: 550 KByte RAM erforderlich (nach HeapRelease).'
                db 13, 10, 36

   ErrTxt2      db 13, 10, 7
                db 'Fehler: 80386 CPU (oder hher) erforderlich.'
                db 13, 10, 36

   ErrTxt3      db 13, 10, 7
                db 'Fehler: CPU befindet sich nicht im Real Address Mode.'
                db 13, 10, 36

   ErrTxt4      db 13, 10, 7
                db 'Fehler: 16 MByte Extended Memory erforderlich.'
                db 13, 10, 36

   ErrTxt5      db 13, 10, 7
                db 'Fehler: EMS-Treiber lokalisiert. Bitte deaktivieren.'
                db 13, 10, 36

   ErrTxt6      db 13, 10, 7
                db 'Fehler: XMS-Treiber lokalisiert. Bitte deaktivieren.'
                db 13, 10, 36

   ErrTxt7      db 13, 10, 7
                db 'Fehler: A20-Gate nicht ansteuerbar (KBC-TimeOut).'
                db 13, 10, 36

   ErrTxtTbl    dw 0
                dw o ErrTxt1, o ErrTxt2, o ErrTxt3, o ErrTxt4
                dw o ErrTxt5, o ErrTxt6, o ErrTxt7

 ;---------------------------------------------------------------------------

 ENDIF

 END

 ;--- Snap ------------------------------------------------------------------



 === OPT0001 ================================================================

 Grundstzliches zur Optimierung

 Thomas-Ivo Heinen 2:2449/830.3:

 Einmal ein Kurzdurchlauf durch die wichtigsten Grundlagen:

         Oft lassen sich  gleiche Ergebnisse mit unterschiedlichen  Befehlen
          realisieren.  Dabei  gilt i.d.R.: je krzer, desto besser.  Dement-
          sprechend sind z.B. 5 Byte den 8 Byte vorzuziehen.

         Speicherzugriffe  sind langsam !  Lieber alle  Register  ausnutzen.
          Einige Variablen sind auch machbar,  da diese bei hufigem Gebrauch
          im Cache liegen ...

         Bleibt nicht auf 8086/80286er Niveau! Schaut Euch lieber einmal die
          386er Instruktionen an ... <g>

         Wozu gibt es denn 32-Bit Register ...

 Nun etwas  Prozessorinternas, bei deren  Kenntnis noch  einiges herausgeholt
 werden kann.  Im Folgenden ist fters die Rede von Ticks, Clocks und Cycles.
 Damit sind interne Prozessortakte gemeint.  So hat z.B. ein 66 Mhz-Prozessor
 pro Sekunde 66 Mio. Ticks...

         AGI - der Address Generation Interlock                < alle CPUs >

          Ein sehr unsympathischer Zeitgenosse,  der einem oft  bei Laufzeit-
          optimierungen dazwischenfunkt,  da er einen Befehl 1 Tick lang ver-
          zgert. Ein AGI  tritt auf, wenn  in zwei aufeinanderfolgenden  Be-
          fehlen dasselbe Register verwendet wird. So zum Beispiel:

                ADD EDX,4
                < AGI >
                MOV ESI, [EDX]

          Bei Prozessoren  bis zum 486  mu man damit nur bei aufeinanderfol-
          genden Befehlen rechnen,  der Pentium dagegen kann auch ber bis zu
          drei Befehle hinweg einen AGI generieren. Hierfr ein Beispiel:

                ADD ESI,4           -> 486: 4 Ticks
                POP EBX             -> 586: 3 Ticks statt 2 wegen AGI
                DEC EBX
                MOV EDX, [ESI]

          Vorsicht brigens vor "versteckten" AGIs:

                MOV ESP,EBP
                < AGI >             -> POP benutzt EBP als Pointer...
                POP EBP

          Dazu eine kleine Skizze:

                          Schritt der Pipe           Pipe1  Pipe2
                  Address Calculate/Fetch Operand      C      D    |
                  Execute                              B      A    |
                                                                   V 

                Falls C das Ergebnis von A bentigt, mu Pipe 1 einen Tick
                verzgert werden, um auf A zu warten. Falls C das Ergebnis
                von D bentigt, mu Pipe 2 ZWEI Ticks verzgert werden.

          ACHTUNG:

          Bei AGIs bildet der Cyrix  5x86 sozusagen  eine Ausnahme, da er ein
          Feature namens  Register  Renaming beherbergt.  Nehmen wir folgende
          Befehlsfolge:

                MOV BX,AX
                SHL AX,8

          Sieht nach einem AGI aus ? Falsch ! Es kann auch so gehen:

                MOV BX,AX
                SHL EX,8

          Hier  wurde also AX  in EX umbenannt,  die Befehle knnten beide in
          EINEM Tick, statt in zweien,  ausgefhrt werden. Der Cyrix 5x86 ist
          dazu in der Lage,  da er intern  32 statt 8 Register verwendet.  So
          werden  einige AGIs verhindert ... warum macht Intel das eigentlich
          nicht !?

         IDS - der Instruction Decoding Stall                    < bis 486 >

          Bei Instruktionen mit einer Konstanten od. einem konstanten Displa-
          cement mssen alle CPUs bis zum 486 einen Tick warten. Beispiele:

               MOV LOOPVAR, 248
               MOV DWORD PTR [ESP+4], 1

          Die  Decode-Stage dieser CPU kann  nmlich immer nur EINE Konstante
          bearbeiten. Der Pentium  hat keine  IDSs, da er  die Befehle anders
          abarbeitet.

         RACS - der Register Access CPU Stall                    < bis 486 >

          Wenn bis zu einem 486 das untere Word eines Doublewords modifiziert
          wird, da unmittelbar bentigt wird, wartet ein 486 einen Tick. Ein
          Pentium braucht dies nicht. Beispiel:

                MOV AL,0
                MOV [EBP], EAX

  Der schnelle Spass am Stck - Quickies Marke ASM86FAQ:

         Vermeidet Multiplikationen ! Mit den  Shiftinstruktionen,  ADDs und
          SUBs geht es oft viel schneller (und mit LEA - siehe OPT0004).

                MOV EAX,10
                MUL ECX

          braucht nahezu ewig. Besser ist da:

                MOV ESI,ECX
                SHL ESI,3
                SHL ECX,1
                ADD ECX,ESI

          Das ist zwar lnger, braucht aber weniger Zeit.
          Selbst auf einem Cyrix ist dies schneller -  obwohl dieser generell
          nur 3 Ticks fr eine Multiplikation bentigt,  denn hier werden nur
          2 Ticks verbraucht.  Will man  allerdings mit einer  Zahl multipli-
          zieren, die mehr als 2 Bits gesetzt hat  (-> 11d = 1011b), dann ist
          auf dem 5x86 ein Mul schneller.

         Vermeidet "LOOP"...

          Statt:          Besser:
          LOOP @B         DEC     ECX
                          JNZ     @B

          Das Resultat ist dasselbe, aber es arbeitet weitaus schneller !
          Beispiel von einem Pentium: Variante 1 braucht 8 Ticks, Variante 2
          dagegen nur 5 ...

         MOV statt STOSB

          Auf 486ern ist MOV ES:[DI],?A? einem STOS? vorzuziehen, da dies
          WENIGER Taktzyklen bentigt...

  Lektre: - C't zu Pentium und Cy5x86
           - "Optimizing" von M.Munstelj und L.Micheletto



 === OPT0002 ================================================================

 Code Alignment

 Juergen Thelen 2:2450/645.5:

 Intel empfiehlt bei der Programmierung von universellem Code  (ein- und der-
 selbe Code soll auf 386, 486 und P5 laufen) die Ausrichtung von Sprungzielen
 auf 0MOD16-Boundaries (Adresse MODULO 16 = 0),  falls das Sprungziel 8 Bytes
 oder weniger von einer solchen Boundary entfernt ist.

 Bei exklusivem Code fr 386er bringt Code Alignment gar nichts, da i386er ja
 keinen On-Chip-Cache besitzen (decoupled Prefetch-Unit). Die grten Perfor-
 mance-Gewinne hat das 0MOD16-Alignment auf 486ern,  da sich das Alignment da
 direkt auf die Effizienz der Prefetchbuffer auswirkt (ein 486er besitzt zwei
 16-Byte-Prefetchbuffer).  Auf P5ern bringt das Alignment zwar nicht so viel,
 wie auf 486ern,  da P5er ohne Clock Penalties auch ber Buffergrenzen hinweg
 prefetchen knnen, aber immerhin mehr als auf 386ern... ;)



 Joerg Kubitz 2:240/9301.12:

 386er von AMD  prefetchen jeweils 4 Bytes  auf einmal. Daher bringen Sprnge
 zu 0MOD4 Adressen auf diesen CPUs Performancegewinne.

 Auf einem 486er  braucht es  bis zu 5 extra  Taktzyklen,  wenn der Befehl am
 Ziel eines Sprunges auf zwei Paragraphen (Paragraph = 16 Bytes) aufgeteilt
 ist.

 Zum manuellen Alignment  eignen sich  z.B. folgende NOPs (auf 486ern jeweils
 nur 1 Taktzyklus):

   16bit Code:

     Instruction             Bytes      Opcodes

     nop                      (1)     ; 90H
     xchg ax, ax              (1)     ; 90H
     mov  ax, ax              (2)     ; 89H c0H
     lea  bx, [bx+00]         (3)     ; 8dH 5fH 00H
     lea  bx, [bx+0000]       (4)     ; 8dH 9fH 00H 00H

   32bit Code:

     nop                      (1)     ; 90H
     mov eax, eax             (2)     ; 8bH c0H
     lea eax, [eax+00]        (3)     ; 8dH 40H 00H
     add eax, 00000000        (5)     ; 05H 00H 00H 00H 00H (ndert FLAGS!)
     lea eax, [eax+00000000]  (6)     ; 8dH 80H 00H 00H 00H 00H



 === OPT0003 ================================================================

 Data Alignment

 Juergen Thelen 2:2450/645.5:

 Weitere Performancesteigerungen  lassen sich  durch gezieltes Ausrichten von
 Daten erreichen.  Wie ausgerichtet werden muss, ist dabei von der Datengre
 abhngig:


   1 Byte (Byte, ShortInt)   egal... ;)
   2 Byte (Word, Integer)    innerhalb 0MOD4-Boundary (Offset 0, 1 oder 2)
   4 Byte (DWord, Longint)   auf 0MOD4-Boundary
   8 Byte (QWord, Real)      auf 0MOD8-Boundary


 Wird das Data Alignment nicht eingehalten, verliert man auf 486ern und P5ern
 mindestens 3 Clocks...



 === OPT0004 ================================================================

 Register-Optimierung

 Juergen Thelen 2:2450/645.5

 Nachfolgend einige Optimierungen, die den Umgang mit Registern betreffen. In
 der Hauptsache handelt es sich um Tips fr 32bit-Prozessoren (386+):


    Register lschen:

       Hier ist "XOR reg, reg" empfehlenswert,  da die Instruction krzer ist
       als ein "MOV reg, 0". Allerdings beeinflusst XOR die Flags. Werden die
       Flags noch gebraucht, ist "MOV reg, 0" besser.

    Vorzeichenlose Erweiterung eines Byte:

       Speziell ab 486ern ist "XOR EAX, EAX / MOV AL, ?"  dem  "MOVZX" vorzu-
       ziehen, da "MOVZX" lnger dauert.

       Bei P5ern kommt hinzu,  da "MOVZX" auch nicht pairable ist, was einen
       weiteren Geschwindigkeitsverlust bedeutet.

       Auerdem kann innerhalb von Schleifen eventuell auch das "XOR" entfal-
       len, bzw. nach aussen verlagert werden,  falls die  oberen 24 Bits von
       E?X nicht  von anderen Instructions  in der Schleife verndert werden.
       Die Auslagerung bringt natrlich weiteren Performancegewinn.

    Konstanten in Register laden:

       Mssen Konstanten  innerhalb von Schleifen verwendet werden, empfiehlt
       es sich,  die Konstante nach Mglichkeit  in einem Register zu halten,
       da ein "MOV reg, reg" bei mehrfacher Verwendung schneller als ein "MOV
       reg, imm" ist (486).

    Register auf Null prfen:

       Hier ist "TEST reg, reg" aus mehreren Grnden  die beste Variante. Sie
       ist besser als "AND reg, reg"  und  "OR reg, reg" weil der Zieloperand
       bei "AND" und "OR"  beschrieben wird  (ist bei "TEST" nicht der Fall).
       Dieses Beschreiben knnte  in der nachfolgenden  Instruction  zu einem
       Address Generation Interlock (AGI) fhren (1 Clock Penalty).

       Der Vorteil gegenber  "CMP reg, reg" ist,  da "TEST reg, reg" krzer
       ist.

    LEA, das Allheilmittel... ;)

       - Schnell und kurz bei Kettenoperationen, z.B. "LEA EDI, [EAX+EBX+8]".

       - Quellregister (wie vorstehend EAX, EBX)  bleiben erhalten und mssen
         nicht extra gesichert werden.

       - LEA ist  bei Shifts um 2, 4 und 8 Bits schneller als SHL/SHR (486+).

       - Durch Verwendung von LEA, ADD, SUB und SHL/SHR Sequenzen lassen sich
         viele  Konstantenmultiplikationen  wesentlich schneller  als mit MUL
         durchfhren.

         Beispiel: Grafikmodus 13H (320x200x256 bei a000:0H).  Der Offset von
                   Zeile EBX soll errechnet und in EAX abgelegt werden.

                   lea    eax, [ebx*4+ebx]               ; * 5
                   shl    eax, 6                         ; * 64 (= * 320)

       - Bei all diesen Vorteilen darf natrlich nicht vergessen werden,  da
         bei Einsatz von LEA  die Gefahr eines AGI (1 Clock Penalty) besteht,
         wenn die vorhergehende Instruction ungnstig gewhlt wird.  Bei ent-
         sprechendem Instruction Scheduling aber wohl kein Problem...



 Joerg Kubitz 2:240/9301.12:

    Stacktransfer von Speichervariablen:

       Auf 486ern ist ein "MOV reg, [mem] / PUSH reg" zwei Taktzyklen schnel-
       ler als ein "PUSH [mem]". Gleiches gilt natrlich auch fr POP...



 Thomas-Ivo Heinen 2:2449/830.3:

    Vorzeichenlose Erweiterung eines Word:

       Schnellste Variante: LEA EAX,[SI]

       Leider passen aber zwischen die Klammern  nur wenige Register[kombina-
       tionen], nmlich: BX, BP, SI, DI oder BX/BP+SI/DI+Imm.



 === OPT0005 ================================================================

 Unrolled Loops

 === OPT0006 ================================================================

 Cache-Optimierung (alle CPUs)

 Thomas-Ivo Heinen 2:2449/830.3:

 Der Unterschied zwischen einem Cache-Hit und einem Cache-Miss besteht in et-
 lichen verlorenen Taktzyklen. So sieht die Cache-Grsse in etwa aus:

    386     :    0 kB intern,            64kB-128kB extern
    486     :    4 kB intern,            64kB-512kB extern
    P5      :    8kB Code/Data,         256kB-  1MB extern
    P6      :  256kB-512kB L2, 32kB L1, 256kB-  1MB extern
    Klamath :  256kB-512kB PB, 64kB L1, 256kB-  1MB extern


 Jumps
 

 Bei der Verwendung von  Short Jumps  ist die Chance,  da der komplette Code
 noch im Cache liegt, besonders hoch - die Geschwindigkeit ebenfalls...


 Strip-Mining
 

 Falls  man  grosse  Arrays bearbeitet,  sollte man diese  NICHT  in mehreren
 Durchgngen  komplett  bearbeiten,  sondern diese  in kleineren  "Portionen"
 durchgehen.  Dadurch  bleiben die  Daten  im Cache  und  knnen  ohne  jeden
 Cache-Miss abgearbeitet werden, dieses Prinzip heisst "STRIP-MINING". Um die
 optimale STRIP/STREIFEN-Grsse zu ermitteln, kann man am Start des Programms
 eine Routine einfgen,  die die Geschwindigkeit von 32k, 64k, 128k, 256k und
 512kB durchtestet.  Auf einem Pentium sind 8kB gut,  da damit der Data-Cache
 optimal genutzt wird (der interne Cache arbeitet immer mit der vollen inter-
 nen Taktrate,  whrend der externe  oft nur  mit halber betrieben wird.  Der
 Vorteil von Strip Mining besteht darin,  da die evtl.  ntigen zustzlichen
 Jumps/Loops fr die Abarbeitung der Strips weniger Zeit bentigen,  als auch
 nur ein Cache-Miss...



 Juergen Thelen 2:2450/645.5:

    Prefetch Queue empty (486), Penalty bis zu 3 Clocks

     Sobald im Prefetch Queue  Raum und Zeit  fr eine weitere Cache Line (16
     Bytes) ist, ldt die Prefetch Unit Daten nach. Allerdings rumt die Unit
     Speicherzugriffen immer hhere Prioritt als Cachezugriffen ein.  Blockt
     man nun den Cache mit dauernden Speicherzugriffen, hat die Prefetch Unit
     keine Mglichkeit zwischendurch eine Cache Line wieder aufzufllen.  Die
     Prefetch Unit muss dann  den gesamten Prefetch Queue neu fllen und auf-
     bereiten, was natrlich lnger dauert, als ein Cache Line Update.

     Um diese Situation zu vermeiden, sollte man der Prefetch Unit sptestens
     drei Clocks vor der vollstndigen Prefetch Queue Leerung die Mglichkeit
     geben, eine Cache Line nachzuladen.  Dies kann mit jedem Nicht-Speicher-
     zugriff (also z.B. einem Registerzugriff) erreicht werden.



 === OPT0007 ================================================================

 386er-Optimierung

 Thomas-Ivo Heinen 2:2449/830.3:


 Decode-After-Jump
 

 Nach einem Sprung mu ein 386er-Prozessor  einige Zeit mit dem Decodiern der
 folgenden Instruktion und dem Flushen der Pipeline verbringen. Dadurch ben-
 tigt der erste Befehl  nach einem Sprung EINEN Tick fr JEDE (!) KOMPONENTE,
 also je Prfix, Opcode, mod/rm, SIB, Offset und Konstante.  Auch ein 8/32bit
 Displacement braucht jeweils einen Extra-Tick.


           SCHNELL                               LAAAAHM

   Loop:   INC EDX                         Loop: MOV [EBX+1234h],EAX
           MOV [EBX+1234h],EAX                   INC EDX
           DEC ECX                               DEC ECX
           JNE SHORT Loop                        JNE SHORT Loop

 Die zweite Variante ist im 32Bit-Modus (also ohne Prefixes) ZWEI Ticks lang-
 samer - verrckt, oder ? Also kann man auf diese Weise 386er-Code um einiges
 beschleunigen...


 Befehlslnge
 

 Die Lnge eines Befehls (in Bytes)  spielt auf einem 386er eine gewichtigere
 Rolle als z.B. auf einem 486er,  da ein 80386  KEINEN internen Cache besitzt
 und die Bandbreite beim Zugriff auf das RAM (naturgem) viel, viel geringer
 als bei seinen Nachfahren ist.  Dementsprechend besonders beim 386 auf kurze
 Befehle achten, da diese schneller decodiert und ausgefhrt werden.

 Eine Ausnahme bilden hier aber die String-Befehle, die trotz ihrer Komplexi-
 tt schneller ausgefhrt werden,  insbesondere wenn sie als erste Befehle in
 einer Loop auftauchen (-> keine Prefixe -> 1 Byte -> schnell...).



 === OPT0008 ================================================================

 Diverse Optimierungen - nocheinmal Quickies <g> ...

 Thomas-Ivo Heinen 2:2449/830.3:


 Akkumulator/DS
 
 Es empfielt sich, sooft mglich, den Akkumulator, also AL,AH,AX oder EAX, zu
 verwenden,  da etliche Befehle dafr einen eigenen Opcode haben,  der Befehl
 also ein Byte weniger bentigt. Dazu kommt hufig noch, da diese "speziali-
 sierten" Opcodes erheblich schneller sind. Aus demselben Grund ist auch bes-
 ser,  mglichst hufig das  DS-Segmentregister zu benutzen,  da alle anderen
 Segmentregister einen Prefix  erzeugen und die Befehle meistens deshalb auch
 langsamer werden.


 CDQ-Instruktion
 
 CDQ wird hufig vor Divisionen verwendet, um das EDX-Register zu lschen. Es
 gibt jedoch eine Alternative,  die zwar von der Ticksanzahl genauso schnell,
 jedoch auf einem P5+ pipeable ist, und zwar: MOV EDX,EAX; SAR EDX,31.

 Falls bekannt ist,  das die Zahl immer  positiv ist,  sollte man einfach ein
 XOR EDX,EDX verwenden (Mann, was ein alter Hut...).


 JMP-Instruktion
 
 Short Jumps  sind IMMER  schneller als Long Jumps,  jedoch erzeugen unglck-
 licherweise einige Compiler defaultmig einen Long Jump,  wo er nicht ntig
 wre. Also lieber immer JMP SHORT schreiben - ist zwar mehr Tipparbeit, holt
 aber ab und an vielleicht ein oder zwei Ticks raus <g>...


 Komplexe Instruktionen
 
 Komplexe Instruktionen wie z.B.  LEAVE,  ENTER und LOOP  verbrauchen unntig
 viel Zeit. Es ist daher besser, sie durch Befehlsfolgen einfacherer Instruk-
 tionen  zu umschreiben  (siehe zu LOOP auch OPT0001).  ENTER kann  z.B.  mit
 einer  Befehlsfolge  hnlich "PUSH [E]BP; MOV [E]BP, [E]SP; SUB [E]SP,COUNT"
 besser ersetzt werden (siehe z.B. Source in LSG0004)...


 Displacements
 
 Kurz und bndig: Arbeitet man hufig mit einer Variable und Displacement, so
 ist es besser dieses in ein Register zu laden - Konstante Displacements ver-
 brauchen immer etwas Zeit zum Decodieren.


 REP MOVSD
 
 Vergesst nicht diese Instruktion,  wenn Ihr 32Bit-Blcke  verschieben wollt,
 denn diese Kombination ersetzt die Befehlsfolge

 Rep_Loop: MOV EAX,[ESI]
           ADD ESI,4
           MOV [EDI],EAX
           ADD DI,4
           DEC ECX
           JNE Rep_Loop

 Diese "superoptimierte" Befehlsfolge  braucht auf einem  386 21n-4 Ticks und
 auf einem 486 8n-2 Ticks,  whrend REP MOVSD  nur 7n+2 Ticks braucht.  Obige
 Routine  bentigt 13 Bytes,  ein REP MOVSD nur 2,  das auch EAX unangetastet
 lsst und nicht den Branch Target Buffer des P5+ strapaziert.


 MOV EAX und Konsorten
 
 Hmm, nocheinmal LEA. Um einen Wert in EAX zu schreiben,gibt es eine grssen-
 mssig bessere Mglichkeit:  LEA EAX,[1234h] ist nmlich quivalent zu einem
 MOV EAX,1234h...  Aber Achtung: Einige Editoren mgen LEA mit einer Konstan-
 ten nicht -  d.h. man muss HARDCODEN und die entsprechenden Hexkombinationen
 eintippseln...


 TASM: MOVS WORD PTR
 
 Unter TASM gibt es ein paar lustige Makros, die es erlauben,eine Instruktion
 wie MOVS WORD PTR ES:[DI],ES:[SI] zu einem SEGES; MOVSW zu verkrzen. Derar-
 tige Makros existieren fr alle Segmentprfixe...



 === OPT0009 ================================================================

 Pairing

 === TSR0001 ================================================================

 Grundstzliches zur TSR-Programmierung

 Lennart Groetzbach 2:2432/321.13:



 === SEC0001 ================================================================

 Anti-Debugger-Tips

 Stefan Meisel 2:2480/403.5:

 Vorab mu zu diesem Thema grundstzlich gesagt werden, da es keinen absolut
 sicheren Softwareschutz gibt. Mit viel Geduld und Erfahrung, aber sptestens
 mit Einsatz von Hardware-Debuggern, Code- und Prozessor-Emulatoren lt sich
 jedes  Stck  Software entschlsseln. Das Ziel solcher Verfahren  kann  also
 immer  nur  sein,  das 'Reverse Engineering' so schwer  und  langwierig  wie
 mglich  zu  machen. Oft fhren solche Anstrengungen zu  Instabilitten  des
 fertigen Programms, immer jedoch zu erschwerter Wartbarkeit des Code.

 Der   folgende   Text   ist  eine   freie   bersetzung   einer   englischen
 Originalvorlage  von  Inbar Raz, Eden Shochat, Yossi  Gottlieb  und  anderen
 Autoren  mit dem Titel 'Anti Debugging Tricks, Version 5', die im Fido  frei
 verfgbar  ist.  Bei  der  bersetzung wurden von  mir  an  einigen  Stellen 
 Korrekturen und Vereinfachungen vorgenommen.                                
                                                                            
 Die meisten hier beschriebenen Manahmen zielen auf die typischen  Debugger-
 Interrupts Int 1 (Trace) und Int 3 (Breakpoint). Ein moderner Debugger  lt 
 sich mit diesen Methoden nicht mehr so leicht beeindrucken, aber ich bin der 
 Meinung, da diese Verfahren zunchst grundstzlich bekannt sein sollten.   
                                                                            
 Schon  in der Originalvorlage wurde in den Beispielen zur  Verbesserung  der 
 Lesbarkeit  das  Instruktionenpaar CLI/STI  um  jede  Interrupt-Manipulation 
 weggelassen.  In der Praxis ist dies jedoch keinesfalls  empfehlenswert,  da 
 ein  Hardware-Interrupt  in der Mitte einer solchen Aktion den  Rechner  zum 
 Absturz bringen kann.                                                       
                                                                            
 ---------------------                                                       
 Anti Debugging Tricks                                                       
 ---------------------                                                       
                                                                            
 Anti-Debugging Tricks lassen sich in zwei Hauptklassen unterscheiden:       
                                                                            
 1. Preventive Manahmen                                                     
 2. Selbstmodifizierender Code                                               
                                                                            
 Die meisten Anti-Debugging Tricks werden heutzutage in Viren eingesetzt,  um 
 die  Disassemblierung des Virus zu vermeiden. Beispiele hierfr finden  sich 
 weiter  unten im Text. Auerdem enthalten Software-Schutzprogramme, die  als 
 Kopier- oder Hackschutz Anwendung finden, eine groe Zahl von Finten, um das 
 Knacken der Schutzmechanismen zu erschweren.                                
                                                                            
 1. Preventive Manahmen
 ------------------------

 Grundstzlich  versteht  man unter preventiven bzw.  vorbeugenden  Manahmen
 eine  Programmgestaltung, die es fr den Anwender unmglich macht, den  Code
 zu disassemblieren oder zu tracen.

 1.1 Abschalten von Interrupts

 Das  Abschalten von Interrupts ist wohl die gebruchlichste Form  von  Anti-
 Debugging Tricks. Hier gibt es verschiedene Mglichkeiten:                  
                                                                            
 1.1.1 Maskieren der Hardware Interrupts                                     
                                                                            
 bliches Vorgehen ist, einen Interrupt ber den 8259 Programmable  Interrupt 
 Controller  (PIC) auszuschalten, um ein Tracen des Codes zu verhindern.  Der 
 PIC,  der die IRQ-Leitungen kontrolliert, kann ber Portbefehle an Port  21h 
 gesteuert  werden.  Das  bedeutet, da jeder IRQ zwischen 0  und  7  einzeln 
 abgeschaltet  werden kann. Bit 0 entspricht IRQ0, Bit1 entspricht IRQ1  usw. 
 Nachdem IRQ1 der Tastatur-Interrupt ist, kann das Keyboard abgeschaltet  und 
 damit der Debugger abgehngt werden.                                        
                                                                            
 Beispiel:                                                                   
                                                                            
 CS:0100 E421           IN     AL,21                                         
 CS:0102 0C02           OR     AL,02                                         
 CS:0104 E621           OUT    21,AL                                         
                                                                            
 Nebenbei  bemerkt, kann die Tastatur auch ber den Keyboard Controller  8042 
 (Ports  60h  und  64h) abgeschaltet werden. Der  Port  61h  am  Programmable
 Peripheral Interface (PPI) bietet ev. eine weitere Mglichkeit, funktioniert
 aber nicht immer.

 Beispiel:

 CS:0100 E461           IN     AL,61
 CS:0102 0C80           OR     AL,80
 CS:0104 E661           OUT    61,AL

 1.1.2 Maskieren der Software Interrupts

 Dies  ist  eine ziemlich einfache Manahme, die  darin  besteht,  Interrupt-
 Adressen  zu berschreiben, die von Debuggern blicherweise benutzt  werden. 
 Es  knnen auch andere Interrupts verndert werden, die das  Programm  nicht 
 braucht  oder  deren Auftreten nicht zu erwarten ist. Dabei  sollte  niemals 
 vergessen werden, die ursprnglichen Interrupt-Adressen vor Programmende  zu 
 restaurieren.  Um  einen Vektor zu verndern, empfiehlt  sich  die  manuelle
 Methode, wie unten gezeigt, anstatt Interrupt 21h, Service 25h zu  benutzen.
 Jeder  Debugger,  der  die  Kontrolle ber Int 21h  hat,  knnte  sonst  den
 genderten  Interrupt auf sich selbst zeigen lassen. Das Beispiel zeigt  ein
 Abfangen des Breakpoint Interrupt 3.

 Beispiel:

 CS:0100 EB04           JMP    0106
 CS:0102 0000           ADD    [BX+SI],AL
 CS:0104 0000           ADD    [BX+SI],AL
 CS:0106 31C0           XOR    AX,AX
 CS:0108 8EC0           MOV    ES,AX
 CS:010A 268B1E0C00     MOV    BX,ES:[000C]
 CS:010F 891E0201       MOV    [0102],BX
 CS:0113 268B1E0E00     MOV    BX,ES:[000E]
 CS:0118 891E0401       MOV    [0104],BX
 CS:011C 26C7064C000000 MOV    Word Ptr ES:[000C],0000
 CS:0123 26C7064E000000 MOV    Word Ptr ES:[000E],0000

 1.1.3 berschreiben von Vektoren

 Diese Methode bezieht die Benutzung einer Interrupt-Adresse in die  richtige
 Aktivierung einer Codesequenz mit ein. Eine solche Manahme wie im folgenden
 Beispiel  knnte  dafr eingesetzt werden, ein Stck Code  zu  entschlsseln
 (siehe auch 2.1). ber den Stackpointer werden hier Daten auf der Interrupt-
 Adresse abgelegt. Nachdem die Interrupts 1 und 3 im normalen  Programmablauf
 nicht benutzt oder verndert werden, funktioniert das Programm, solange  man
 nicht versucht, es zu debuggen.                                             
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 31C0           XOR    AX,AX                                         
 CS:0102 8ED0           MOV    SS,AX                                         
 CS:0104 BC0E00         MOV    SP,000E                                       
 CS:0107 2E8B0E3412     MOV    CX,CS:[1234]                                  
 CS:010C 50             PUSH   AX                                            
 CS:010D 31C8           XOR    AX,CX                                         
 CS:010F 21C5           AND    BP,AX                                         
 CS:0111 58             POP    AX                                            
 CS:0112 E2F8           LOOP   010C                                          
                                                                             
 1.1.4 Austausch von Interrupts                                              
                                                                             
 Dies ist ein ziemlich schmutziger Trick,  der nur eingesetzt werden  sollte,
 wenn man sich vllig sicher ist,  da man sein Programm nicht mehr  debuggen
 mu.  Hierbei werden die Adressen von hufig benutzten Interrupts, z.B.  Int
 16h  und  Int 21h, auf die Adressen von Int 1 und Int 3, die  im  Normalfall
 nicht  gebraucht werden, kopiert. Der Anwender, der dieses  Programm  tracen
 will,  mu jede Stelle, an der ein Int 1 auftritt, gegen den  entsprechenden
 Interrupt austauschen. Erschwerend ist der Umstand, da Int 3 durch einen 1-
 Byte Opcode (CCh) dargestellt werden kann. Dies macht den direkten Austausch
 gegen jeden anderen Interrupt unmglich.                                    
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 FA             CLI                                                  
 CS:0101 31C0           XOR    AX,AX                                         
 CS:0103 8EC0           MOV    ES,AX                                         
 CS:0105 26A18400       MOV    AX,ES:[0084]                                  
 CS:0109 26A30400       MOV    ES:[0004],AX                                  
 CS:010D 26A18600       MOV    AX,ES:[0086]                                  
 CS:0111 26A30600       MOV    ES:[0006],AX                                  
 CS:0115 B44C           MOV    AH,4C                                         
 CS:0117 CD01           INT    01                                            
                                                                             
 1.2 Zeitkontrolle                                                           
                                                                             
 Diese Methode ist weniger gebruchlich, aber ntzlich gegen den Einsatz  von
 Debuggern,  die  alle  Interrupts  auerhalb  der  Laufzeit  des   Programms
 abschalten,  wie z.B. der Turbo Debugger von Borland. Hierbei  wird  einfach
 der  Wert  eines Zeitzhlers, der sich mit Int 8 verndert,  ausgelesen.  In
 einer  Endlosschleife wird dann solange gewartet, bis sich der Wert  ndert.
 Ein anderes Beispiel besteht darin, den Timer Interrupt IRQ 0 zu  maskieren,
 indem  der  Wert  aus Port 21h gelesen, mit  1  Oder-verknpft  und  zurck-
 geschrieben  wird.  (Einige Takte spter mu man dies  natrlich  rckgngig
 machen.)                                                                    
                                                                             
 CS:0100 2BC0           SUB    AX,AX                                         
 CS:0102 FB             STI                                                  
 CS:0103 8ED8           MOV    DS,AX                                         
 CS:0105 8A266C04       MOV    AH,[046C]                                     
 CS:0109 A06C04         MOV    AL,[046C]                                     
 CS:010C 3AC4           CMP    AL,AH                                         
 CS:010E 74F9           JZ     0109                                          
                                                                             
 1.3 Debugger verwirren                                                      
                                                                             
 Diese  nette  Technik  wirkt speziell und nur  gegen  Turbo  Debugger  sowie
 hnliche  Produkte.  Hier  wird ein Sprung in die  Mitte  einer  Instruktion
 gelegt, wobei die direkt nachfolgende Adresse einen anderen Opcode  enthlt.
 Debug  oder SymDeb lassen sich hiermit nicht tuschen, weil sie zum  exakten
 Sprungziel verzweigen, whrend der Turbo Debugger im  Einzelschrittmodus  an
 den Anfang der nchstliegenden Adresse 0106 springt.                        
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 E421           IN     AL,21                                         
 CS:0102 B0FF           MOV    AL,FF                                         
 CS:0104 EB02           JMP    0108                                          
 CS:0106 C606E62100     MOV    Byte Ptr [21E6],00                            
 CS:010B CD20           INT    20                                            
                                                                             
 Wobei das Sprungziel ist:                                                   
                                                                             
 CS:0108 E621           OUT    21,AL                                         
                                                                             
 Hinweis:  Dieser  Trick  beeinflut  den  Ablauf  des  Programms  in  keinem
 Debugger.  Er  dient  nur dem Zweck, den Anwendur  ber  den  wahren  Opcode
 hinwegzutuschen.                                                           
                                                                             
 1.4 berprfen der CPU Flags                                                
                                                                             
 Dieser Trick kann gegen fast jeden Real Mode Debugger eingesetzt werden. Man
 mu  nur  das  Trace  Flag  irgendwo  im  Programm  lschen  und  es  spter
 berprfen. Wenn es eingeschaltet ist, luft ein Debugger im Hintergrund.   
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 9C             PUSHF                                                
 CS:0101 58             POP    AX                                            
 CS:0102 25FFFE         AND    AX,FEFF                                       
 CS:0105 50             PUSH   AX                                            
 CS:0106 9D             POPF                                                 
                                                                             
 Und spter im Programm:                                                     
                                                                             
 CS:1523 9C             PUSHF                                                
 CS:1524 58             POP    AX                                            
 CS:1525 250001         AND    AX,0100                                       
 CS:1528 7402           JZ     152C                                          
 CS:152A CD20           INT    20                                            
                                                                             
 1.5 Debugger anhalten                                                       
                                                                             
 Diese  Technik  lt  Debugger  bei  der  Ausfhrung  bestimmter   Programme
 anhalten. Erreicht wird dies, indem man Int 3 Befehle zufllig ber den Code
 verstreut,  an  denen der Debugger stoppen mu. Die beste  Wirkung  wird  in
 Schleifen erzielt.                                                          
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 B96402         MOV    CX,0264                                       
 CS:0103 BE1001         MOV    SI,0110                                       
 CS:0106 AC             LODSB                                                
 CS:0107 CC             INT    3                                             
 CS:0108 98             CBW                                                  
 CS:0109 01C3           ADD    BX,AX                                         
 CS:010B E2F9           LOOP   0106                                          
                                                                             
 1.6 Stack Manipulationen                                                    
                                                                             
 Diese   Methode  geht  davon  aus,  da  viele  Debugger  den  Stack   ihres
 Zielprogramms mitbenutzen. Im folgenden Beispiel wird der Stack in die Mitte
 eines Programmcodes gelegt, der selbst keinen Stack bentigt. Als Folge wird
 der   Debugger   einen   Teil  des  Programmcodes   durch   seinen   eigenen
 Stackverbrauch   berschreiben   (hauptschlich  durch   Interrupt   Return-
 Adressen).  Mit weniger drastischen Auswirkungen als im Code lt  sich  der
 Stackpointer auch in die eigenen Daten legen. Die CLI und STI Anweisungen im
 Beispiel drfen nicht weggelassen werden, sonst hngt sich das Programm auch
 ohne Debugger auf.                                                          
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 8CD0           MOV    AX,SS                                         
 CS:0102 89E3           MOV    BX,SP                                         
 CS:0104 0E             PUSH   CS                                            
 CS:0105 17             POP    SS                                            
 CS:0106 BC0B01         MOV    SP,010B                                       
 CS:0109 90             NOP                                                  
 CS:010A 90             NOP                                                  
 CS:010B EB02           JMP    010F                                          
 CS:010D 90             NOP                                                  
 CS:010E 90             NOP                                                  
 CS:010F 89DC           MOV    SP,BX                                         
 CS:0111 8ED0           MOV    SS,AX                                         
                                                                             
 1.7 TD386 im V86 Mode anhalten                                              
                                                                             
 Dies ist eine Methode, das V86 Modul (TD386) des Turbo Debuggers zu stoppen.
 Es  beruht auf dem Umstand, da der TD386 nicht den Int 0 benutzt, um  einen
 Division  by Zero-Error (oder Registerberlauf  nach Division, der  von  der
 CPU genauso behandelt wird wie eine Division durch Null) zu melden. Wenn der
 TD386 einen Divisionsfehler entdeckt, endet er unter Angabe der fehlerhaften
 DIV Instruktion. Im Real Mode (und auch unter den konventionellen Debuggern)
 wird eine fehlerhafte Division den Interrupt 0 auslsen. Folglich, falls der
 Int 0 auf die nachfolgende Instruktion zeigt, luft das Programm weiter.

 Es  ist  natrlich notwendig, Int 0 sofort zu restaurieren, sonst  wird  der
 nchste Aufruf von Int 0 zum Absturz fhren.                                
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 31C0          XOR     AX,AX                                         
 CS:0102 8ED8          MOV     DS,AX                                         
 CS:0104 C70600001201  MOV     WORD PTR [0000],0112                          
 CS:010A 8C0E0200      MOV     [0002],CS                                     
 CS:010E B400          MOV     AH,00                                         
 CS:0110 F6F4          DIV     AH                                            
 CS:0112 B8004C        MOV     AX,4C00                                       
 CS:0115 CD21          INT     21                                            
                                                                             
 1.8 Jeden V86 Prozess anhalten                                              
                                                                             
 Eine andere Mglichkeit, den TD386 auszuschalten, ist, ihn in eine Exception
 zu  treiben.  Allerdings  wird  diese Exception  auch  unter  jedem  anderen
 Programm,  das im V86-Mode luft, ausgelst. Die Exception ist #13  und wird
 durch  Interrupt 0Dh ausgelst. Die Idee hnelt dem Trick mit  der  Division
 durch  Null:  Man  lst eine Exception aus, whrend  der Exception Interrupt
 irgendwo  in den Programmcode zeigt. Das funktioniert nur im Real-Mode,  nie
 aber im V86-Mode.                                                           
                                                                             
 Auch   hier  gilt  wieder:  Die  originale  Interrupt  Adresse  mu   sofort
 restauriert werden, sonst hngt die nchste Exception die Maschine auf.
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 31C0          XOR     AX,AX                                         
 CS:0102 8ED8          MOV     DS,AX                                         
 CS:0104 C70634001301  MOV     WORD PTR [0034],0113                          
 CS:010A 8C0E3600      MOV     [0036],CS                                     
 CS:010E 833EFFFF00    CMP     WORD PTR [FFFF],+00                           
 CS:0113 B8004C        MOV     AX,4C00                                       
 CS:0116 CD21          INT     21                                            
                                                                             
 2. Selbstmodifizierender Code                                               
 -----------------------------                                               
                                                                             
 2.1 Verschlsselung/Entschlsselung                                         
                                                                             
 Die erste Kategorie ist einfach verschlsselter Code, dem eine  Entschlsse-
 lungsroutine  vorgeschaltet  wurde.  Die Methode erschwert  das  Setzen  von
 Breakpoints. Wenn durch einen Debugger ein Breakpoint gesetzt wird, schreibt
 er  blicherweise an die gewnschte Adresse den Opcode CCh (Int  3).  Sobald
 der  Int  3  ausgefhrt wird, erhlt der Debugger  die  Kontrolle  ber  das
 System.  Nachdem  die  Entschlsselungsroutine  aus  zahlreichen  Schleifen-
 befehlen  besteht,  ist  die  Versuchung gro,  den  Breakpoint  hinter  die
 Entschlsselungsroutine  zu setzen. Dort aber wird der Code modifiziert  und
 der Breakpoint zu einer undefinierten Instruktion verdreht.                 
                                                                             
 Das  folgende  Beispiel stammt aus dem Haifa Virus. Ein  Breakpoint  an  der
 Stelle  CS:110 wird niemals erreicht, weil das Resultat der  Entschlsselung
 nicht  vorhersagbar  ist. Um das Tracen des Codes zu  erschweren,  wird  die
 Entschlsselung  am Ende des Programmcode begonnen, so da  alle  Schleifen-
 operationen  durchgemacht  werden mssen, bis der Opcode am  Ende  der  Ent-
 schlsselungsroutine sichtbar wird.                                         
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 BB7109         MOV    BX,0971                                       
 CS:0103 BE1001         MOV    DI,0110                                       
 CS:0106 91             XCHG   AX,CX                                         
 CS:0107 91             XCHG   AX,CX                                         
 CS:0108 2E803597       XOR    Byte Ptr CS:[DI],97                           
 CS:010C 47             INC    DI                                            
 CS:010D 4B             DEC    BX                                            
 CS:010E 75F6           JNZ    0106                                          
 CS:0110 07             POP    ES                                            
 CS:0111 07             POP    ES                                            
                                                                             
 2.2 Selbstmodifizierender Code                                              
                                                                             
 2.2.1 Einfache Modifizierung                                                
                                                                             
 In  dieser Methode wird das selbe Prizip wie bei der  Verschlsselung  ange-
 wandt:  Der Opcode wird berschrieben, bevor er benutzt wird.  Im  folgenden
 Beispiel  verndern wir den Code, der hinter dem Call liegt. Deshalb,  falls
 der  ganze  Call  bersprungen  wird ('p' bei debug  oder  'F8'  beim  Turbo
 Debugger)  wird  man hier die Kontrolle verlieren, denn der  Debugger  setzt
 sein CCh an Adresse 103h und die Subroutine berschreibt diesen Opcopde.    
                                                                             
 Beispiel:                                                                   
                                                                             
 CS:0100 E80400         CALL   0107                                          
 CS:0103 CD20           INT    20                                            
 CS:0105 CD21           INT    21                                            
 CS:0107 C7060301B44C   MOV    Word Ptr [0103],4CB4                          
 CS:010D C3             RET                                                  
                                                                             
 und das ist der Inhalt der Subroutine:                                      
                                                                             
 CS:0103 B44C           MOV    AH,4C                                         
                                                                             
 2.2.2 Die 'Running Line' (Selbstentschlsselung)                            
                                                                             
 Hier  kommt  noch ein recht ausgefallenes Beispiel fr  einen  selbstmodifi-
 zierenden  Code,  der  sich selbst traced. Er  wurde  von  Serge  Pachkovsky
 vorgestellt und ist unter dem Namen 'The Running Line' bekannt. Er ist nicht
 einfach  zu implementieren, aber ziemlich  widerstandsfhig gegen  Versuche,
 die Interrupttabelle zu schtzen. Die Instruktionen werden immer nur einzeln
 entschlsselt, dadurch wird nie eine lngere Codesequenz der Analyse  preis-
 gegeben. Das folgende Codefragment stellt das Verfahren vereinfacht dar.    
                                                                             
 Beispiel:                                                                   
                                                                             
 XOR     AX, AX                                                              
 MOV     ES, AX                                                              
 MOV     WORD PTR ES:[4*1+0],OFFSET TRACER                                   
 MOV     WORD PTR ES:[4*1+2],CS                                              
 MOV     BP, SP                                                              
 PUSHF                                                                       
 XOR     BYTE PTR [BP-1], 1                                                  
 POPF                                                                        
 MOV     AX, 4C00H               ;Wird nicht getraced!                       
 DB      3 DUP ( 98H )                                                       
 DB      C5H, 21H                                                            
                                                                             
 TRACER:                                                                     
                                                                             
 PUSH    BP                                                                  
 MOV     BP, SP                                                              
 MOV     BP, WORD PTR [BP+2]                                                 
 XOR     BYTE PTR CS:[BP-1], 8                                               
 XOR     BYTE PTR CS:[BP+0], 8                                               
 POP     BP                                                                  
 IRET                                                                        
                                                                             
 ----------------------------                                                
                                                                             
 Soweit also der Text von Inbar Raz. Anmerkungen sind ausdrcklich erwnscht. 
 Weitere Artikel zu diesem Thema sollen folgen, sobald sie verfgbar sind.



 === SEC0002 ================================================================

 Verschlsseln von Daten

 Bastiaan Zapf 2:241/1149.77:

 Das Ziel  einer Verschlsselung ist es,  Daten so zu manipulieren,  dass sie
 nur noch fr Personen,  die ber einen  bestimmten Wissensstand (nmlich den
 den Schlssel und das Verschlsselungsverfahren) verfgen, erkennbar sind.

 Die Datenverschlsselung ist besonders fr den Shareware-Programmierer (ver-
 stecken des Registrierungshinweises o..) und den Spiele-Programmierer (ver-
 schlsselung der Spielstnde gegen Patching) wichtig.

 Generell gibt es mehrere Methoden, Daten zu verschluesseln:


    Vertauschungsalgorithmen
    Ein-Schlssel-Verschlsselung
    Zwei-Schlssel-Verschlsselung
    (Einwegverschlsselung)


 Vertauschungsalgorithmen:

   Vertauschungsalgorithmen sind zwar effektiv, da sie in den seltensten Fl-
   len erwartet werden,  sind jedoch zugleich relativ leicht zu knacken, wenn
   man den Algorithmus kennt.

   Das Grundprinzip ist die Vertauschung einzelner Datenelemente in einer be-
   stimmten Reihenfolge.  Die Entschlsselung  wird dann  durch die Umkehrung
   dieser Vertauschung erreicht.

   Ich fhre hier  kein Code-Beispiel an,  da effiziente  Algorithmen schnell
   bermssige Dimensionen erreichen. brigens: Schon die Griechen verwendet-
   en solche Algorithmen, wobei sie z.B. ein Lederband auf einen Stab wickel-
   ten und dieses parallel zum Stab beschrifteten.  Wenn das Band abgewickelt
   und  als fortlaufender Text  betrachtet wird,  haben alle Buchstaben  ihre
   Pltze gendert.


 Ein-Schlssel-Verschlsselung:

   Ein-Schlssel-Verschlsselung  ist die  gebruchlichste  und zugleich, bei
   vertretbarem Aufwand, sicherste Methode.

   Das Grundprinzip ist,  da jedes Datenelement  mittels eines Schlssels zu
   einem Datenelement  umgewandelt wird,  welches man mit demselben Schlssel
   wieder zurckverwandeln kann. Ein einfaches Beispiel ist es, zu jedem Byte
   ein  bestimmtes Byte  hinzuzuaddieren.  Die Entschlsselung erfordert dann
   eine Subtraktion. Beispiel:


     -+-+-+-+-+-+-+] Bitte hier knabbern [+-+-+-+-+-+-+-
     ; ds:si    sei auf die zu verschlsselnden Daten gerichtet
     ; es:di    sei auf freien Speicher in gleicher Gre gerichtet
     ; cx       Anzahl der zu verschlsselnden Bytes
     ; dl       Schlssel

     ver_schl:
             lodsb
             add al,dl
             stosb
             loop ver_schl

    ; Der Entschlsselungsalgorithmus luft genauso, blo wird statt 'add'
    ; 'sub' verwendet
    -+-+-+-+-+-+-+] Bitte hier knabbern [+-+-+-+-+-+-+-


   So einen  Algorithmus  verwendete  auch Julius Caesar,  der  anstatt jedes
   Buchstaben dessen Nachfolger im Alphabet benutzte. Wenn man jetzt den Code
   etwas ndert,  kann man Schlssel  von Word-Lnge  oder  sogar DWord-Lnge
   benutzen. Wenn man ganz raffiniert ist,  ist es sogar mglich,  Schluessel
   beliebiger Lnge einzusetzen.

   Dieses Verschlsselungssystem hat jedoch einen gravierenden Nachteil: Wenn
   man lange Ketten  gleicher Zeichen  hat,  hat man  im verschlsselten Text
   auch lange Ketten gleicher Zeichen.

   Wenn man bespielsweise mit der Julius-Caesar-Methode den Text "AAAAA" ver-
   schlsselt, hat man als Produkt "BBBBB". Das mag zwar bei kurzen, normalen
   Daten nicht weiter auffallen,  aber wenn man  z.B. Texte verschlsselt (in
   denen ja  z.B. das 'E'  berdurchschnittlich hufig vorkommt),  lsst sich
   durch statistische Untersuchungen der Schlssel ermitteln.

   Die einzige Mglichkeit,  diesen Weg zu erschweren,  ist der Einsatz eines
   lngeren Schlssels, oder, was am Ende auf dasselbe hinauskommt, die mehr-
   fache Anwendung des Algorithmus'  mit verschiedenen Schlsseln;  am besten
   mit Schlsseln, deren Lngen nicht durcheinander teilbar sind.


 Zwei-Schlssel-Verschlsselung:

   Zwei-Schlssel-Verschlsselungen sind nur mit sehr komplizierten Algorith-
   men durchzufhren,  die jedoch alle auf der 'Einseitigkeit' mancher mathe-
   mathischer Operationen beruhen.

   Beispielsweise ist es  relativ einfach 26^7 zu rechnen,  es ist jedoch ex-
   trem schwer, die einzigen beiden ganzen Zahlen, die miteinander potenziert
   8031010176 ergeben, zu finden.

   Bei diesen Verschlsselungsalgorithmen  werden aus einem Satz pseudozufl-
   liger Daten zwei Schlssel ermittelt.  Einer davon kann frei verteilt wer-
   den, und obwohl alle diesen Schlssel haben knnen,  ist es unmglich, mit
   ihm verschlsselte Daten zu entschlsseln.  Nur mit dem  zweiten Schlssel
   ist das mglich.


 Einwegverschlsselung:

   Einwegverschlsselungen sind keine 'echten' Verschlsselungen, sie sind im
   Prinzip nur zur Sicherung der Integritt von Daten geeignet.  Dabei werden
   einzelne Teile  eines Datenelements miteinander  oder  mit einem Schlssel
   kombiniert,  wobei das Ergebnis  ein sehr kleines Datenelement ist. Dieses
   Datenelement wird gespeichert.

   Falls die Datenintegritt in Frage gestellt wird, wird der Algorithmus er-
   neut durchlaufen  und das Ergebnis  mit dem gespeicherten Wert verglichen.
   Wenn  die Daten  in der Zwischenzeit  verflscht wurden,  sollten sich die
   beiden Werte voneinander unterscheiden.

   Ein gutes Beispiel fr solch ein Verfahren ist das Parittsverfahren,  das
   als Ergebnis pro Byte ein Bit liefert, das so gesetzt ist,  da die Anzahl
   der gesetzten Bits im Byte plus das Parittsbit - je nachdem, ob man gera-
   de oder ungerade Paritt verwendet - gerade oder ungerade ist.

   Ein anderes  hufig eingesetztes Verfahren ist das CRC-Verfahren,  auf das
   hier jedoch nicht eingegangen wird,  da die Umsetzung ziemlich umfangreich
   ausfllt.



 === SEC0003 ================================================================

 Key Generation

 === ADD0001 ================================================================

 Assembler Pakete

 === ADD0002 ================================================================

 Disassembler Pakete

 === ADD0003 ================================================================

 Debugger Pakete

 === ADD0004 ================================================================

 MASM: Patch von 6.11 auf 6.11a

 Raymond Moon, raymoon@dgs.dgsys.com

 Laut Raymond soll auf ftp.microsoft.com ein Patch namens ML611A.EXE  im Ver-
 zeichnis softlib/mslfiles liegen (Stand 27.09.94, von mir nicht geprft).

 Bei dieser Datei  soll es sich um ein SFX-File (selbstextrahierend) handeln,
 das unter anderem eine Datei namens ML611A.TXT enthlt.  Die ML611A.TXT soll
 wiederum  die Anleitung zur Installation des Patches von MASM 6.11 auf 6.11a
 enthalten.

 // An die MASM-Benutzer:  Da ich MASM nicht verwende, ist mir nicht bekannt,
 // welche Version  von MASM  derzeit aktuell ist.  Sollte dieses Topic ber-
 // holt sein, bitte ich (FAQ-Keeper) um entsprechende Nachricht. Danke... 8)



 === QLL0001 ================================================================

 Thema: Allgemeine Systemprogrammierung

 Dateien:

    Ralf Brown's Interruptlist

      * gut sortierte Mailboxen mit Schwerpunkt Programmierung
      * (http|ftp)://x2ftp.oulu.fi/pub/msdos/programming/docs/inter*.*

      Aktuelle Version: 51 (Stand: 28.08.96)

    HelpPC

      * gut sortierte Mailboxen mit Schwerpunkt Programmierung
      * (http|ftp)://x2ftp.oulu.fi/pub/msdos/programming/docs/helppc*.*

      Letzte Version: 2.1 (Autor verstorben)

    PC Games Programmers Encyclopedia

      * gut sortierte Mailboxen mit Schwerpunkt Programmierung
      * (http|ftp)://x2ftp.oulu.fi/pub/msdos/programming/gpe/pcgpe*.*

      Aktuelle Version: 1.0 (Stand: 28.08.96)

    Robert Collins' "What Intel doesn't want you to know (tm)" Topics

      * http://www.x86.org
      * ftp://x86.org/pub/x86/*.*

 Bcher:

    PC Intern 3.0
    Tischer, Michael
    Data Becker
    ISBN 3-89011-591-8

    PC Intern 4.0
    Tischer, Michael
    Data Becker
    ISBN 3-81581-094-9

    PC Hardware
    Hans-Peter Messmer
    Addison Wesley
    ISBN 3-89319-710-9

    80386/486 Handbuch fr Programmierer
    Nelson, Ross P.
    Verlag unbekannt
    ISBN 3-86063-200-0



 === QLL0002 ================================================================

 Thema: Allgemeine Algorithmen

 Dateien:

    -

 Bcher:

    Fundamental Algorithms (The Art of Computer Programming, Vol.1)
    Knuth, Donald E.
    Addison-Wesley
    ISBN 0-201-03809-9

    Seminumerical Algorithms (The Art of Computer Programming, Vol.2)
    Knuth, Donald E.
    Addison-Wesley
    ISBN 0-201-03822-6



 === QLL0003 ================================================================

 Thema: Allgemeine Grafikprogrammierung

 Dateien:

    -

 Bcher:

    Grundlagen der Computergraphik
    Foley, von Dam, Feiner, Hughes, Philips
    Addison-Wesley
    ISBN 3-89319-647-1



 === QLL0004 ================================================================

 Thema: Assemblerpakete

 Dateien:

    Eric Isaacson's A86

      * gut sortierte Mailboxen mit Schwerpunkt Assembler-Programmierung
      * (http|ftp)://x2ftp.oulu.fi/pub/msdos/programming/lang/a86v*.*

      Aktuelle Version: 4.01 (Stand: 25.07.96)

 Bcher:

    -



 === QLL0005 ================================================================

 Thema: Programmieren in Assembler

 Dateien:

    -

 Bcher:

    Das Assemblerbuch
    Autor unbekannt
    Addison Wesley
    ISBN 3-89319-853-9



 === XXXXXXX ================================================================

 Hufig verwendete Abkrzungen:

 Am*        American Megatrends Prozessoren
 ANSI       American National Standard Institute
 API        Application Programming Interface
 APIC       Advanced Programmable Interrupt Controller
 APM        Advanced Power Management
 ARLL       Advanced Run-Length Limited
 ASCII      American Standard Code for Information Interchange
 AT         Advanced Technology
 ATA        Advanced Technology Adapter
 ATAPI      Advanced Technology Adapter Packet Interface
 ATC        Attribute Controller
 AVATAR     Advanced Video Attribute Terminal Assembler & Recreator

 BASIC      Beginners All Purpose Symbolic Instruction Code
 BCD        Binary coded decimal
 BIOS       Basic Input/Output System
 BIT        Binary digit
 BPB        BIOS Parameter Block
 BPI        Bits per Inch
 BPS        Bits per Second
 BTB        Branch Target Buffer

 CAD        Computer Aided Design
 CAM        Computer Aided Manufacturing
 CAPI       Communicating Application Programming Interface
 CAS        Communicating Applications Specification
 CD         Compact Disc
 CDROM      Compact Disc Read Only Memory
 CDWORM     Compact Disc Write Once Read Many
 CGA        Color/Graphics Adapter
 CGI        Common Gateway Interface
 CMOS       Complementary Metal Oxide Silicon/Semiconductor
 CNC        Computerized Numeric Control
 CPI        Characters per Inch
 CPL        Current Privilege Level
 CPS        Characters per Second
 CPU        Central Processing Unit
 CRC        Cyclical Redundancy Check
 CRT        Cathode Ray Tube
 CRTC       Cathode Ray Tube Controller
 Cx*        Cyrix Prozessoren

 DAC        Digital/Analog Converter
 DGIS       Direct Graphics Interface Standard
 DIP        Dual Inline Package
 DLL        Dynamic Link Library
 DMA        Direct Memory Access
 DOS        Disk Operating System
 DPI        Dots per Inch
 DPL        Descriptor Privilege Level
 DPMI       DOS Protected Mode Interface
 DPMS       DOS Protected Mode Services
 DRAM       Dynamic Random Access Memory
 DTA        Disk Transfer Area
 DTP        Desktop Publishing

 ECC        Error Correction Code
 EDO        Extended Data Out
 EEPROM     Electronical Erasable Programmable Read Only Memory
 EGA        Enhanced Graphics Adapter
 EIDE       Enhanced Integrated Drive Electronics
 EISA       Enhanced Industry Standard Architecture
 EMS        Expanded Memory Specification
 EOF        End of File
 EOI        End of Interrupt
 EOT        End of Text
 EPROM      Erasable Programmable Read Only Memory
 ESDI       Enhanced Small Device Interface
 EV86       Enhanced Virtual 86 Mode

 FAQ        Frequently asked questions
 FAT        File Allocation Table
 FCB        File Control Block
 FDC        Floppy Disk Controller
 FM         Frequency Modulation
 FOSSIL     Fido/Opus/Seadog Standard Interface Layer
 FPU        Floating Point Unit
 FTP        File Transfer Protocol

 GDT        Global Descriptor Table
 GUS        Gravis Ultrasound

 HGC        Hercules Graphics Card
 HMA        High Memory Area
 HPFS       High Performance File System
 HTML       Hypertext Markup Language
 HTTP       Hypertext Transfer Protocol

 i*         Intel Prozessoren
 IC         Integrated Circuit
 IDE        Integrated Drive Electronics (Hardware)
            Integrated Development Environment (Programmierung)
 IDT        Interrupt Descriptor Table
 IFS        Installable File System
 IOCTL      Input/Output Control
 IOPL       Input/Output Privilege Level
 IPX        Internetwork Packet Exchange
 IRC        Internet Relay Chat
 IRQ        Interrupt Request
 ISA        Industry Standard Architecture
 ISDN       Integrated Services Digital Network
 ISR        Interrupt Service Routine

 JFT        Job File Table

 LAN        Local Area Network
 LCD        Liquid Crystal Display
 LDT        Local Descriptor Table
 LED        Light Emitting Diode
 LOL        List of Lists
 LPT        Line Printer
 LQ         Letter Quality
 LSB        Least significant bit

 M1         Cyrix 6x86
 M1SC       Cyrix 5x86
 M5         Cyrix Cx486S/D
 M6         Cyrix Cx486DX
 M7         Cyrix 486DX2
 M8         Cyrix 486DX4
 MCA        Microchannel Architecture
 MCB        Memory Control Block
 MCGA       Multi Color Graphics Adapter
 MDA        Monochrome Display Adapter
 MFLOPS     Million Floating Point Operations per Second
 MFM        Modified Frequency Modulation
 MIDI       Musical Instrument Digital Interface
 MIPS       Million Instruction per Second
 MMX        Multimedia Extensions
 MSB        Most significant bit
 MSR        Model Specific Register
 MSW        Machine Status Word
 MTBF       Mean Time between Failures

 NCB        Network Control Block
 NDIS       Network Driver Interface Specification
 NLQ        Near Letter Quality
 NMI        Non Maskable Interrupt
 NPX        Numerical Processor Extension
 NTBA       Network Termination Base Adapter
 NTFS       New Technology File System
 NVRAM      Non Volatile Random Access Memory

 OCR        Optical Character Recognition
 ODI        Open Datalink Interface
 OEM        Original Equipment Manufacturer

 P24C       Intel i486
 P24T       Intel Pentium Overdrive (Socket 3 (5.0V))
 P24CT      Intel Pentium Overdrive (Socket 3 (3.3V))
 P5         Intel Pentium (60/66)
 P54C       Intel Pentium (75/90/100)
 P54CS      Intel Pentium (120/133)
 P54CSQ     Intel Pentium (120+)
 P6         Intel PentiumPro
 PCI        Peripherial Component Interconnect
 PIC        Programmable Interrupt Controller
 PIO        Parallel Input/Output
 PIT        Programmable Interval Timer
 PoP        Point of Presence
 POP        Post Office Protocol
 POST       Power-On Self-Test
 PPP        Point-to-Point Protocol
 PSP        Program Segment Prefix

 RAM        Random Access Memory
 RGB        Red-Green-Blue
 RLL        Run-Length Limited
 ROM        Read Only Memory
 RPL        Requestor Privilege Level
 RTC        Real Time Clock

 SB         Soundblaster (Creative)
 SCSI       Small Computer Systems Interface
 SDA        Swappable Data Area
 SEOI       Specific End Of Interrupt
 SFT        System File Table
 SIM        Single Inline Module
 SIP        Single Inline Package
 SLIP       Serial Line Interface Protocol
 SMM        System Management Mode
 SQL        Structured Query Language
 SRAM       Static Random Access Memory
 SVGA       Super Video Graphics Array

 TLB        Translation Lookaside Buffer
 TOM        Top of Memory
 TPI        Tracks per Inch
 TPA        Transient Program Area
 TS         Timer Sequencer
 TSR        Terminate and stay resident
 TSS        Task State Segment
 TTY        Teletype

 UART       Universal Asynchronous Receiver/Transmitter
 UMB        Upper Memory Block
 URL        Uniform Resource Locator

 V86        Virtual 86 Mode
 VBE        VESA BIOS Extension
 VBE/AI     VESA BIOS Extension / Audio Interface
 VCPI       Virtual Control Program Interface
 VDS        Virtual DMA Specification
 VESA       Video Electronics Standards Association
 VFAT       Virtual File Allocation Table (?)
 VGA        Video Graphics Array
 VRML       Virtual Reality Modeling Language

 WAIS       Wide Area Information System
 WWW        World Wide Web

 XGA        Extended Graphics Array
 XMS        Extended Memory Specification
