ENFÜHRUNG:


...In zwei Übersetzungsprogramme für Assembler-Quellprogramme, die im NASM-Dialekt geschrieben wurden und deren eines NASM-Dialekt übersetzt und deren anderes einen neuen Dialekt übersetzt, den ich am Ende dieses Textes beschreibe. Das Programm, das den NASM-Dialekt übersetzt, wird im folgenden "ASMn" genannt und ist in einiger Hinsicht vereinfacht, weil es nur unter ASMOS arbeiten soll und nur Programme für dieses Betriebssystem übersetzen können soll. Es ist dafür gedacht, bereits vorhandene, in NASM geschriebene Programme mit nur wenigen Änderungen unter ASMOS assemblieren zu können. Falls die Quellkompatibilität nicht nötig ist, sollte man den Abkömmling "ASMat" benutzen, den ich weiter unten beschreibe.
Für ASMn gilt, dass eine grosse Anzahl von Ausdrücken nicht übersetzt bzw.ignoriert wird, die von NASM erkannt werden. Und es gibt einige Dinge, die in ASMn möglich sind, nicht aber in NASM... Eine detaillierte Gegenüberstellung der Kommandos findet man in der Datei "TSOURCE", die auch zur Testübersetzung und Verifizierung der Codes taugt.

ASMat:


...ist ein einziges standalone-Program, das in einem einzigen Durchlauf assembliert.
Die Ausgabe des Übersetzungsprogramms sind ein Listing und ein Binary, das sofort mit den normalen Menüfunktionen unter ASMOS zum Laufen gebracht werden kann. Die Fehlerausgabe erfolgt im Listing, indem eine Zeile geschwärzt wird, undzwar exakt ab dem Ausdruck oder Teilausdruck, der nicht mehr erkannt wird. Normalerweise wird trotz solcher Fehlererkennung weiter gelistet. Nur ein doppelt definiertes Label, ein negativer Adresswert beim Vervielfachen ("TIMES") oder eine zu lange Adresse im Kommando "loop" führen zum sofortigen Abbruch der Übersetzung.
Die Präprozessorstufe ist so einfach wie möglich gehalten - sie existiert nicht. Da in ASMOS Ausschneiden&Kopieren sehr einfach zu machen sind und eine Kopie beliebige Zeichen 00h-FFh enthalten kann, braucht man nur die ohnehin vorhandenen Funktionen und weder "include" noch irgendwelche "Makros".
Da man eine Codeübersetzung für den real mode höchstens noch für die Programmierung eines Bootloaders braucht (der eigentlich schon geschrieben wurde), enthält ASMn nichts, was real mode- oder V86-Programmierung erlaubt. ASMn arbeitet also mit implizitem, unabänderlichem "USE 32".
Da die einzige Umgebung, in dem die Quellprogramme arbeiten können sollen, ASMOS ist, gibt es weder irgendwelche besonderen Linker-header zur Wahl noch irgendwelche Optionen beim Start von ASMn - nach der Initialisierung ist ASMn ein Menüpunkt unter F9.
Der ASMn-assembler erkennt sehr wenige "expressions", und diese sind auf wenige Zwecke in Definitionen beschränkt: (arithmetische Anweisungen sind bis auf einen Spezialfall ausgeschlossen!):
: + * -
Ein Label kann mit Postfix ":" oder ohne wie in NASM geschrieben werden. Es kann auch ein Kommando in der gleichen Zeile folgen.
ASMn beginnt das Assemblieren einer Zeile, indem es zunächst ein Semikolon ";" sucht und dabei "whitespace" (Zwischenräume =20h oder =00h) überspringt. Neben diesen besonderen drei Zeichen wird jedes andere zunächst als Teil eines Mnemoniks oder des Ausdrucks "ALIGN 4" (einem "standard macro" in NASM, das in ASMn eine Pseudo-Mnemonic ist) bewertet. Ähnlich wie NASM und andere Assembler, wird erst nach einer Nichterkennung irgendeines Mnemoniks der Name eines Labels als Sinn der Zeichenkette angenommen.
Wenn "ALIGN 4" oder "align 4 " gefunden wird, füllt ASMn mit NOPs (=90h) bis zur nächsten durch 4 teilbaren Byteadresse.
Hinter einem Label wird wie gesagt zunächst kein Mnemonik gesucht, sondern eines der folgenden sieben Zeichen:
DT, DQ, DD, DW, DB (gross oder klein geschrieben, um den Typ zu definieren)
oder einer der zwei quasi-ops "*" (ähnlich "TIMES") oder "I" (ähnlich "INCBIN").
Alle anderen Zeichen werden dagegen als Teil eines Mnemoniks genommen.
Falls der Typ DB ist, können wie in NASM statt Ziffern 'text...' oder "text..." folgen. Und ebenso wie im NASM-Stil kann ein Name eines Labels eine Adresse repräsentieren, wenn der Typ DD ist.
Nur die Typen DT, DQ und DD dürfen Werte mit einem Minuszeichen als Präfix definieren. Während aber der Typ DD auch "Integer" (also reelle Zahlen) definieren kann, müssen die Typen DQ und DT grundsätzlich Fliesskommawerte definieren, die ich am Ende dieses Textes genauer erkläre. Die Definition diese Werte erfolgt in der (glücklicherweise) allgemein akzeptierten Form.
Da die Anweisungen "TIMES" und "INCBIN" in AMSn als Typangaben gehandelt werden, gibt es keinen Zweck für "$" oder "$$". Wenn der Typ eines Labels "*" ist, führt ASMn ein Füllanweisung ähnlich "TIMES" aus. Die Anweisung hat dann noch einen Zählerwert für Wiederholung zu enthalten - Das sieht etwa so aus:

Füllmit: * 200h D 20202020h ;...füllt mit 400h ASCII-spaces beginnend bei "Füllmit" (ein Label!) Als Typ der Füllwerte darf nur D oder W oder B in diesem Ausdruck benutzt werden!
Statt dem Zählerwert für die Wiederholung kann auch eine Adressdifferenz verwendet werden, in der aber nur ein einziges Minuszeichen erlaubt ist! Das sieht dann etwa so aus:
Füll_bis_Adresse: * Offsetadresse - 0
...wobei "Offsetadresse" eine Ziffer zu sein hat, das Minuszeichen implizit die Adresse des Labels dieser Anweisung enthält (ganz wie "$" in NASM), der Typ der Füllwerte grundsätzlich DB ist (und deshalb nicht geschrieben werden muss), und also nur noch der Wert des Füllbytes - hier eine 0 - geschrieben werden muss. Natürlich sind keine negativen Resultate der Adressdifferenz erwünscht! Deshalb bricht ASMn bei sowas die weitere Übersetzung ab.
Der Typ "*" darf nur zum Füllen verwendet werden. Spielzeug wie "TIMES 100 movsb"...ist nur zu haben, wenn man den Opcode in den Füllwert schreibt. Tatsächlich kann aber mit meiner sparsamen Ausführung alles gemacht werden, was mit "TIMES" in NASM möglich ist, wobei Klammern oder "$" aber überflüssig werden.
Der Typ "I" oder "i" arbeitet im Effekt wie "INCBIN". In den Editier-Modi von ASMOS kann man einfach Rohdaten kopieren und einfügen. Also ist nur noch eine Länge des eingefügten Rohdatensatzes nötig, was dann etwa so aussieht:
Einzufügende_bytes: I 100h x.... Das sagt AMSn, dass 100h Bytes beginnend mit "x" als definierte Werte in das Binary übertragen werden sollen.
Sowas kann man nur mit dem Editier-Modus 0 unter ASMOS machen. Diese Methode des Einfügens erlaubt neben irgendwelchen Rohdaten natürlich auch Programmcode (aus einem Binary). Man kann also auch real mode Code einfügen, Schliesslich kann man auf diese sehr einfache Weise Text für Textausgaben und beliebige Tabellen definieren...
Anders als in NASM sind keine vorangestellten Segment-Überschreibungs-Praefixe erlaubt (z.B. "es"). Diese Präfixe des Opcodes müssen grundsätzlich innerhalb einer effektiven Adresse ( = das, was zwischen eckigen Klammern steht) definiert werden.
Innerhalb eines Kommandos dürfen nur folgende Spezialausdrücke stehen, und auch nur an einer Stelle:
DWORD WORD BYTE SHORT NEAR FAR
Sowas darf nur zwischen Mnemonik und Operandenteil stehen und durch exakt zwei Whitespaces abgesetzt!
Sprünge innerhalb eines Segmentes (insbesondere die bedingten Sprünge!) werden in den NEAR-Typ übersetzt. Irgendein SHORT wird zwar nicht als Fehler abgetan, aber ignoriert, wenn das Sprungziel nicht bereits bekannt ist (also davor im Text steht). Ist das Sprungziel aber bereits bekannt, wird ebenso ignorant automatisch ein SHORT Sprung versucht.
Das erspart jede Menge blödsinnige Fehlermeldungen, die NASM absondert, und hat nur den leisen Nachteil, das evtl. drei Byte mehr Opcode übersetzt werden - belanglos wegen dem Opcode-prefetch der CPU (und bei modernen CPUs im ns-Bereich).
Innerhalb eckiger Klammern [...] einer effektiven Adresse sind neben Weisszeichen ebenfalls nicht erlaubt:
BYTE, WORD, DWORD, NOSPLIT
Ausserdem hat man sich innerhalb einer effektiven Adresse genauso klar auszudrücken, wie die CPU das braucht, und ohne Zwischenräume: Die CPU generiert nämlich jede effektive Adresse aus genau vier Teilen:
base, index, scale-factor, displacement
Deshalb hat jeder Operand dieser Art im Prinzip so auszusehen:
[segmentoverride:base+index*scalefactor+displacement]
segmentoverride kann der Name eines der Segmentregister sein: ds,es,fs,gs,ss
base and index kann der Name eines der 32-stelligen Register sein: eax,ebx,ecx,edx,esi,edi,esp,ebp
scalefactor kann nur sein: 2,4 oder 8
displacement kann als Ziffer oder Name einer Adresse ausgedrückt werden und auch negativ sein.
ABER es darf nur eine Ziffer oder ein Name auftreten! Die in NASM erlaubten Rechenanweisungen werden als Fehler markiert! Die Abfolge der möglichen Teile einer solchen effektiven Adresse darf nicht anders sein, aber das eine oder andere Teil darf wegbleiben. Nur eins kann dann noch irritieren: Ein erster Registername kann Basis- oder Indexregister definieren. Hier verfolgt ASMn die Strategie, dass ein vorhandener scalefactor das Register als Indexregister kennzeichnet. Andernfalls ist es das Basisregister. Ist also nur ein Basisregister definiert, wird in die kurze Form ohne displacement überetzt. Anders ist das, wenn esp oder ebp Teil der effektiven Adresse sind. Beide Registercodes haben spezielle Zwecke bei der Abarbeitung durch die CPU.
Die Umarbeitung von bereits geschriebenem NASM-Code kann sehr einfach sein (z.B.bei meinen Programmen). Eventuell muss aber auch die Religion gewechselt werden, weil jeder Ausdruck, den ASMn nicht verwertet, entfernt werden muss. Man kann sich aber mit einem Probelauf das als Fehler markieren lassen, was ASMn nicht akzeptiert ( und ich als Blödsinn verwerfe). Jedenfalls müssen alle "TIMES"- und "INCBIN"-Ausdrücke umformuliert werden und alle Makros aufgelöst sein!
Falls irgendwelche Ausdrucksweisen nicht im Kopf des Programmierers liegen, hilft ein Blick in die Datei "TSOURCE", wo eine Gegenüberstellung der Schreibweisen in NASM, ASMn und ASMat zu finden ist. Die Datei diente mir als Testquelltext beim Schreiben der Assembler und enthält jedes erkennbare mnemonic sowie die Varianten für den jeweiligen Operandenteil, die genügen, die richtige Übersetzung aller Varianten einzuschätzen (und die Fehlererkennung).

ASMnr:


Anders als NASM übersetzt ASMn ja keinen gemischten Quellcode für sowohl real als auch protected mode. Für einige, wenige Zwecke (Bootloader) ist gemischter Code in einer Quelltextdatei aber wünschenswert.
Ein Übersetzungsprogramm für beide Modi wäre sehr kompliziert wegen nötiger Verzweigungen an beinahe jeder Ecke. Ich habe deshalb die Aufgabe auf zwei Programme verteilt mit dem zusätzlichen Effekt, dass jedes für sich seine Aufgabe schneller und mit weniger Speicherplatzbedarf erledigt. Die Verknüpfung verschiedener Codes ist unter ASMOS sehr einfach, weil man im Editier-Modus 0 eine binäre Kopie erzeugen und einfügen kann. Diese Aufgabe muss also nicht im Übersetzungsprogramm gelöst werden!
Das praktische Vorgehen dabei und die Feinheiten der Programmierung für den real mode entnimmt man den Kommentaren in ASMnr und der Originaldatei dieses Textes. Es gibt sehr deutliche Unterschiede zum protected mode!

ASMat:


Dieser Abkömmling entstand, weil ich während der Arbeit an ASMn sah, dass man das Programm nicht nur etwas einfacher machen konnte, indem man den Quelldialekt ändert, sondern dass man so vor allem mal die Erzeugung der Quelle vereinfachen kann. Dieses Programm mit seinem Dialekt ist dann vorzuziehen, wenn man keine Kompatibilität zu anderen Betriebssystemen braucht. Der Dialekt von ASMat ist nur unter ASMOS zu haben.
Einige Vorteile seien kurz hervor gehoben. Die anderen erscheinen beim Umgang mit dem Assembler und seiner Heimat ASMOS.
-> wie ASMn auch, selektiert ASMat zuerst nach den am häufigsten verwendeten Mnemoniks - "mov" und Sprünge...
-> ASMat selektiert nach Labels aber nicht erst (wie alle anderen Assembler), wenn fast dreihundert Mnemoniks nicht zutrafen. Weil Label nicht gerade selten in einem Programm vorkommen, ergibt sich allein dadurch ein sehr grosse Beschleunigung der Übersetzung. Ausserdem hat man bei der Namensbildung nicht mehr zu bedenken, ob irgendwas am Namen als Kommando verkannt werden kann.
-> ASMat enthält weitere Vereinfachungen in der Schreibweise von Typen und Operandenteilen.
-> Weil beinahe jedes Assemblerprogramm bis zu 50% "mov"-Kommandos enthält, habe ich als Alternative zu den drei Buchstaben den einen nach links weisenden Winkel eingebaut, der rechts von der linken Schift-Taste ohne Akkordgriffe gesetzt werden kann. Ausserdem habe ich die Kleinschreibung zum Zwang gemacht und alle Bezeichner eliminiert, die einem Programmierer das Schreiben sauer machen können.
Die drei Dimensionen eines Programmes: Label, Kommandos und Kommentar werden nach einer sehr einfachen Regel geschrieben:

Dies ist Kommentar, weil das erste Zeichen in der Zeile weder ein Weisszeichen noch ein Doppelpunkt ist. :"Dies" ist ein Label namens "Dies", während der Rest der Zeile Kommentar ist (nach zwei Weisszeichen!)..... mov eax,ebx ist ein Kommando, weil mindestens ein Weisszeichen am Zeilenanfang steht. Es geht also ohne Kommentarzeichen! ASMat sucht zunächst nach Weisszeichen (20h oder 00h) und Doppelpunkten, und falls beides nicht zutrifft, wird die Zeile als Kommentar abgehakt. Leere Zeilen erscheinen nicht im Listing.
Ist das erste Zeichen ein Doppelpunkt, wird jedes Zeichen danach als Teil eines Namens bewertet, bis dass ein Weisszeichen es scheidet vom Rest der Zeile. In diesen Namen sollten, weil sie ja auch Teil einer effektiven Adresse sein können, nur wenige Zeichen nicht verwendet werden, denen in dieser Beschreibung besondere Ausdruckskraft verliehen wird. Es können aber insbesondere auch solche Zeichen verwendet werden, die üblicherweise garnicht zu tippen sind, in ASMOS aber schon.
Weil Label sowohl die Adresse eines Opcodes ausdrücken können, als auch die eines Binärwertes, muss ein zweites Weisszeichen dem Namen folgen, wenn der Name die Adresse eines Opcodes kennzeichnet. Andernfalls muss eines der Zeichen
h t q d w b i * : zu finden sein. Falls nichts dergleichen gefunden wird, wird geschwärzt. Alles, was danach kommt, hat so zu erscheinen wie oben für ASMn beschrieben - mit Ausnahmen:
-> Labelnamen, die zu Adresskonstanten werden sollen, müssen als Präfix einen Doppelpunkt erhalten, der also ohne Zwischenraum vor dem Namen steht, während die anderen Typen mit einem Zwischenraum vom nächsten Teilausdruck abgesetzt werden müssen, z.B.:
:Adresskonstante :derenName Sinnvollerweise kopiert man nämlich solche oft unaussprechlichen Dinger, und dann eben gleich mit Doppelpunkt...
-> Irgendwelche Sachen zwischen Gänsefüsschen sind ein Fehler - stattdessen wird der Typ "i" Zwang, z.B.:
:Zeichenkette i 50h definiert eine Zeichenkette mit einer Länge von 50h Bytes Nun noch einige Beispiele für häufig benutzte Definitionen von Konstanten:
:DWORD d FFh ist ein 32-stelliger Binärwert, wobei die Hexadezimalziffer nicht mit Präfix 0 geschrieben werden muss :word w 10 ist ein mit Dezimalziffer definierter 16-stelliger Binärwert :Byte b 101b ist ein mit Dualziffer definierter 8-stelliger Binärwert Die Definition von Konstanten im Fliesskommaformat wird unten im Zusammenhang mit dem Fliesskommarechner erklärt. Die Typangaben für diesen Zweck sind d, q oder t
Für genaueste Definition von Werten zwischen 1 und 0 gibt es in ASMat noch den Typ "h", der Fliesskommawerte mittels hexadezimaler Ziffern zu definieren gestattet. Der Exponent wird dabei als Potenz von 16 statt 10 genommen.
Ist das erste Zeichen in einer Zeile ein Weisszeichen, werden folgende Weisszeichen übersprungen und ein Mnemonik wird erwartet. Dies muss in kleinen Buchstaben geschrieben werden, ebenso wie die Namen von Registern oder Sonderzeichen. Nur in Hexadezimalziffern sind auch grosse ABCDEF erlaubt (...weil die mir so gut gefallen).
Innerhalb von Kommandos sind ebenfalls Vereinfachungen gegeben:
Die schwierig zu greifende öffnende, eckige Klammer wird durch einen Punkt ersetzt. Die schliessende Klammer ist vollkommen überflüssig und wird deshalb auch nicht ersetzt, sondern verboten. Der Ausdruck SHORT ist ebenso überflüssig wie NEAR und wird ebenfalls ersatzlos gestrichen. Die oben unter ASMn beschriebene Vorgehensweise macht es möglich. Typangaben wie DWORD sind nur in einem Fall nötig - wenn ein Festwert in den Speicher geschrieben soll. Die Buchstaben d w b werden also in der Übersetzung als Kennzeichnung von Immediate-Operanden verwertet und dürfen deshalb auch nur dann vorkommen! Die Buchstaben bilden dann ein Präpräfix einer effektiven Adresse wie z.B.:
mov d.eax+ebx*4-name,123h ,womit das schreiben eines 32-stelligen Wertes in den Speicher kommandiert wird Die Unterscheidung zwischen Sprüngen und Rufen innerhalb eines Segmentes und solchen in andere Segmente muss unterscheidbar bleiben. Statt den drei Buchstaben FAR wird aber für ASMat nur ein kleines f geschrieben, undzwar ebenfalls als Präpräfix, weil die Kommandos, die die Unterscheidung nötig machen, stets eine effektive Adressdefinition enthalten - etwa so:
jmp f.es:eax+ebx*2-10h entspricht: jmp FAR [es:eax+ebx*2-10h] im NASM-Dialekt Falls man, wie ich empfehle, den Quellcode im Editier-Modus 0 schreibt, kann man sehr einfach Kommandos zu Kommentar machen, weil man dann auch in Spalten rauf oder runter schreiben kann. Man kann auch ein Listing sehr einfach wieder zum Quelltext machen, indem man die ersten Spalten mit der mittels AltGr-verschobenen TAB-Taste löscht (unter dem linken Bildrand verschwinden lässt).
Um Unklarheiten zu beseitigen, lese man die Datei "TSOURCE", wo in jeder Zeile NASM-, ASMn- und ASMat-Ausdruck für das gleiche Kommando geschrieben stehen.
Schliesslich sei noch denen gesagt, die die sonst üblichen Schnörkel schmerzlich vermissen, dass man sich selbstverständlich seinen eigenen Präprozessor schreiben kann ohne an ASMat auch nur eine Zeile ändern zu müssen. Weil beliebig bedeutsame Zeichen ausser Weisszeichen und Doppelpunkten am Anfang einer Zeile stehen können und damit von ASMat komplett ignoriert werden, kann man jede Menge Schnörkelkram ausdenken, den ein Präprozessor dann umsetzt bevor ASMat das ignoriert. Ein sehr viel besserer Weg zu besonderem Umgang mit Code ist in ASMOS aber durch beliebig definierbare Dateiköpfe gegeben! Dann kann sogar eine Fehlerkontrolle oder sonstwas in die Tastaturabfrageschleife einbinden, automatisch angelautete Mnemoniks ergänzen oder die Namensbildung automatisieren. Ich werde mich allerdings für sowas nicht ins Zeug legen...

FPU:


Besondere Umgangsformen sind für die Programmierung des Arithmetik-Prozessors ("Floating Point Unit"=FPU) nötig, die nicht allein mit der Kenntnis der Mnemonics zu leisten sind. Deshalb erkläre ich hier möglichst kurz Fliesskommaformat und Bedeutung der Kommandos, die neben dem Arbeitsspeicher Register nicht innerhalb der CPU, sondern der FPU adressieren.
Ausführlicher sind die folgenden Angaben in der Originaldatei enthalten.
Die FPU ist ab CPUs vom Typ >= i486 Teil der CPU und kann mit eigenen Registern auch parallel zur CPU arbeiten. Um die Synchronisation zu gewährleisten und Fehler im Rechengang zu melden, kann die FPU selbst Unterbrechungen veranlassen. Sie tut das aber nicht wie die CPU und auch nicht über den Interrupt-Controller, sondern im Zusammenhang mit dem eigenen Statusregister.
Weil die FPU eine eigenständige Recheneinheit ist, die einstmals auch in separatem Gehäuse untergebracht wurde, hat sie ihren eigenen Ablauf von Reset und Initialisierung. Der wird allerdings im wesentlichen über das Steuer-Register cr0 in der CPU erledigt.
Ursprünglich enthält die FPU acht 80-stellige Datenregister, die als Stapel mit einem FPU-internem Stapelzeiger adressierbar sind, der die Bits 11,12,13 im Statusregister der FPU belegt, auf das unterste belegte Element (=TOP) zeigt und abwärts "wächst".
In neueren FPUs sind absolut adressierbare und anders benamte Operanden-Register und auch neue Kommandos sind zu haben, die aber aber in keinem meiner Übersetzungsprogramme erkannt werden.
Ein wesentlicher Teil der Konstruktion ist ein Operandenformat, das eine binäre Repräsentation von Komma, Mantisse (die in diesem Zusammenhang auch "Signifikant" genannt wird) und Exponent erlaubt. Dieses Format ist unter "IEEE 754" genormt und im Internet zu finden.
Während Mantisse und Exponent mit dreierlei Stellenzahl vorkommen können und nur innerhalb der jeweiligen Stellenzahl variieren können, kann das Komma in der Mantisse "fliessen".
Damit ist gemeint, dass die Stelle, nach der das Komma erscheint, nicht direkt durch eine binäre Repräsentation wie z.B.das Vorzeichen in der CPU definiert ist, sondern durch Definition des Exponenten zustande kommt. Dieser hat ebenfalls keine Stelle für ein Vorzeichen, sondern ist mittels eines fest eingebauten "Bias" um Null herum definierbar. Jeweils die niedere Hälfte der Dualziffer, die den Exponenten definiert, ist dadurch als negativer Exponent gegeben.
Initialisierung und Steuerung werden über drei Register erledigt:
cr0 in der CPU intialisiert und steuert das Zusammenspiel zwischen CPU und FPU und stellt die FPU ein.
Steuer-Register in der FPU, nur implizit mit Kommandos ;fldcw;, ;fstcw; und ;finit; zu erreichen
Status-Register in der FPU, nur implizit mit Kommandos ;fstsw; und ;fnstsw; zu erreichen
Für den laufenden Betrieb ist natürlich das 16-stellige Status-Register unverzichtbar, das in den Stellen 0-5 die FPU-flags enthält, die mit Unterbrechungsanforderungen verknüpft sind und über das Steuerregister maskiert und damit von der Wirkung abgeschnitten werden können. Diese Stellen zeigen die "Ausnahmen" (="exceptions") an, die bei der Interruptbehandlung als reparierbar zu betrachten sind, also nicht zum Systemzusammenbruch führen sollten. Der Interrupt-Behandler muss auch unbedingt das entsprechende flag in der FPU löschen! Es wird nämlich nicht automatisch zurückgesetzt und würde ungelöscht nicht aufhören, Ausnahmen zu fordern.
Die Programmierung eines geeigneten Interrupt-Behandlers für den Interrupt 10h (16) ist also unbedingt nötig, bevor auch nur ein FPU-Kommando gegeben wird! Auch die Behandlung eines Interrupts 7 ("FPU nicht verfügbar / Taskumschaltung") ist eventuell sehr wichtig. Der Interrupt 6 schliesslich würde gegeben, wenn neuere FPU-Kommandos (mmx usw...) gegeben aber nicht verarbeitbar sind.
In den Bits 8,10,14 werden dagegen die Zustände gezeigt, die denen im Status-Register der CPU entsprechen.
Schliesslich wird in den Bits 11,12,13 die aktuelle Adresse des Stapelzeigers dargestellt, der die Operanden-Register der FPU adressiert.
Operanden können auch im Arbeitsspeicher liegen, nicht aber in CPU-Registern. Der richtige Platz sind aber FPU-Register, die als Stapel organisiert sind:
Während die Adresse des Stapelzeigers ein Register absolut adressiert, kann ein Register in Kommandos nur relativ zu dieser Adresse adressiert werden. Der Name der Register ist dabei in den meisten Assembler-Dialekten "st" mit einem Postfix von 0...7. Da das Postfix aber relativ auf das vom Stackzeiger augenblicklich adressierte Register bezogen wird, hat also immer das den Namen "st0" was den "TOP" des Stapels bezeichnet!
Da der Stackzeiger inkrementiert oder dekrementiert werden kann, kann man die Relation verschieben, macht also aus ein und dem selben Register "st7" oder "st1" und hat TOP verschoben. Somit kann man den relativ adressierbaren Stapel mit seiner tatsächlichen Basisadresse =TOP durch den Stapel rotieren lassen, was raffiniert ausgenutzt werden kann.
Die FPU kann Zahlen grundsätzlich nur im Fliesskommaformat verwenden, undzwar mit 80 Stellen. Operanden für einfache und doppelte Genauigkeit werden immer entsprechend umgewandelt! Dabei wird das auch in der CPU benutzte Dualziffernsystem zur Repräsentation von Exponent und Mantisse benutzt.
Auf jeden Fall ergeben sich Genauigkeitsprobleme in der FPU wegen der begrenzten Stellenzahl für Mantisse und Exponent. Es gibt also eine grösste und eine kleinste darstellbare Zahl.
Die Struktur der drei möglichen Formate, die mit Angabe ihrer "Präzision" unterschieden werden, ist zwar einigermassen einfach, nicht jedoch die Wandlung in Dezimalziffern, die weitere Ungenauigkeiten bringt.
Der von mir in ASMn/ASMat vorgestellte Algorithmus zur Wandlung dezimaler Konstantendefinitionen in das Fliesskommaformat ist neu und arbeitet präziser als andere bekannte Algorithmen in Hochsprachen. Er enthält schöne Beispiele für den ansich einfachen Umgang mit Werten, die in mehr als 64 Stellen definiert sind, falls multipliziert, dividiert oder potenziert werden muss. Einen wichtigen Teil des Rechenganges erledige ich in 128 Stellen, womit kein Hochsprachen-Programmierer umgehen kann. Deshalb unterscheiden sich die mit ASMn/ASMat übersetzten Konstanten eventuell in den letzten Stellen der 64 von denen, die NASM oder andere Übersetzungsprogramme bei gleicher Definition übersetzen.

DAS FLIESSENDE KOMMA:


Wer mit dem Gebrauch von Zehnerpotenzen vertraut ist, hat auch schon ein Komma fliessen gesehen, allerdings sicherlich ohne den blumigen Ausdruck dafür...
Wir kennen die Zehnerpotenzen als 10=10exp2, 100=10exp2 usw... und können diese als Multiplikator von Dezimalziffern benutzen. Dabei ist, was links von "exp" steht, die Mantisse. Rechts davon steht der Exponent, der die Potenz ausdrückt (und wenn's geht hoch gesetzt geschrieben wird).
Dann ist 123*10exp1=12,3*10exp2=1,23*10exp3=0,123*10exp4.....und dabei ist das Komma von rechts nach links geflossen. Völlig analog fliesst das Komma (das von Amerikanern und anderen als Punkt geschrieben wird) bei Dualziffern, die in der FPU wie der CPU bestens durch Spannungen an Transistoren repräsentiert werden können (0V für 0 dual oder 5V für 1 dual ). Aber wenn nun das Komma fliesst, multipliziert bzw. dividiert man nicht mit dem Faktor 10 wie im Dezimal-system, sondern nur mit dem Faktor 2 .In beiden Systemen ist das aber ein Inkrementieren bzw. Dekrementieren des Exponenten.
Nun kann klar sein, dass man mit der Wahl des Exponenten das Komma in einer Mantisse setzen kann, wenn man wie in einer FPU nur eine begrenzte, als Genauigkeit ausgedrückte Zahl von Stellen hat.
Den richtig definierten Exponenten muss ein Programmierer aber garnicht einschätzen, weil die FPU nämlich selber das Komma fliessen lassen kann, indem der Exponent inkrementiert bzw.dekrementiert wird. Dabei wird eine Anordnung von Ziffern benutzt, die in der Norm IEEE-754 festgeschrieben wurde. Damit werden die Mathematik reeller Zahlen und maschinelle Gegebenheiten sehr elegant zu einer Regel gemacht.
Grundsätzlich wird jede Ziffer in eine Form gebracht, die nur noch Nachkommastellen enthält. Auch wenn man Konstanten in einem Assemblerprogramm anders formulieren kann, muss bei der Übersetzung der Ausdruck aufbereitet werden. Damit erübrigt sich schon mal eine binäre Repräsentation des Kommas, denn natürlich wird in der FPU nicht mit Dezimalziffern, sondern Dualziffern umgegangen. Dabei gibt es immer eine führende 1 ,die allein vor dem Komma steht, wenn die FPU damit umgeht. Eine Definition von 1234 wird also zu 1,234*10exp3
Weil die binäre Repräsentation dieser Zahl mit der Basis 2 statt 10 umgeht, muss natürlich der Multiplikator anders aussehen. Dann sieht das Beispiel so aus: (1,234*5exp3)*2exp3
Damit kann der Exponent bleiben, wie er ist, die Mantisse aber ist mit 5exp3 zu multiplizieren. Und natürlich müssen die Dezimalziffern in Dualziffern gewandelt werden. Der Ausdruck "2exp" dagegen ist in die FPU eingebaut.
Weil bei dezimaler Konstantendefinition stets die potenzierte 5 im Spiel ist, die bei negativen Exponenten als Divisor der Mantisse auftritt, sind die gewandelten Dualziffern nur ausnahmsweise "glatt". Weil irgendwelche irrationalen Brüche natürlich rationaler werden müssen, um in die FPU zu passen, müssen Stellen abgeschnitten werden, die tatsächlich definiert wurden. Was immer dann damit gerechnet wird, ist also ein bisschen daneben, wenn die Mantissendefinition nicht glatt durch alle Fünfen teilbar ist, die der Exponent gebietet!
Aus diesem Grunde mache ich in ASMat die hexadezimale Definition von Fliesskommawerten mit Typ "h" möglich.
Nach der Wandlung in Dualziffern gibt es eine führende 1, die exakt mit dem Exponenten der Zweierpotenz ausgedrückt ist, und die deshalb auch nur gedacht werden kann. Sie wird bei allen Werten mit einfacher oder doppelter Genauigkeit erst nach der Wandlung in erweiterte Genauigkeit ausgepackt. Diese versteckte führende 1 kennzeichnet "normalisierte" Mantissen, die bei gegebener Stellenzahl doppelt so viele Nachkommastellen haben können. Stets wird diese 1 als einzige Vorkommastelle abgehandelt.
Nur wenn der Exponent den kleinstmöglichen Wert hat, wird "denormalisiert", was das Fehlen der ansonsten impliziten 1 bedeutet. Diese Denormalisierung macht natürlich eine andere Behandlung des Resultates nötig und wird deshalb mit Unterbrechungsanforderung laut gemacht. Weil das viel Zeit kostet, werden normalisierte Mantissen nicht bei erweiterter Genauigkeit benutzt. Am besten sollte man sie auch ausserhalb der FPU benutzen, wenn Speicherplatz kein Problem darstellt.
Ähnlich pfiffig ist das Problem des Vorzeichens für den Exponenten gelöst worden. Das sagt, wenn es ein Minuszeichen ist, dass mit der potenzierten Zahl nicht multipliziert wird, sondern dividiert. Für die Darstellung von Nullkommafastnix ist das also dringend nötig! Deshalb wird je nach Genauigkeit ein Versatz (="bias") zum Exponenten addiert. Den wahren Exponenten erhält man also stets erst durch Subtraktion des bias. Der ist bei einfacher Genauigkeit =127, bei doppelter =1023 und bei erweiterter =16383
Wer diese Zahlen in Dualzifferndarstellung kennt, wird gleich das Gefühl haben, dass hier was fehlt - und tatsächlich! Diese Exponenten, die nur aus Nullen oder Einsen bestehen, sind besonderen Zwecken geweiht. Sie stellen die Werte für unendlich dar oder NICHT-Zahlen (=Not a Number =NaNs). Es sind nämlich ausser Ziffern auch andere, nicht als Ziffern darstellbare Resultate zu erwarten.
Ausser solchen fremdartigen Werten sind neben abgeschnittenen (nicht definierten) Stellen ausserdem noch Rundungsfehler zu berücksichtigen, die Resultate nicht wirklich exakt machen. "Unendliche" Werte können tatsächlich exakte endliche sein, die nur zu klein oder zu gross geraten sind und deshalb nicht mehr in die begrenzte Zahl von Stellen (insbesondere des Exponenten) passen.
Die fremdartigen Werte werden von der Unterbrechungsbedingung, z.B "ungültige Operation"=IE begleitet.
Bei Definitionen ist zu beachten, dass das "e" in der üblichen Darstellung nicht nur den bezifferten Exponenten definiert, sondern für einen Multiplikator steht, der eine mit dem Exponenten potenzierte 10 ist: 1.e3 ist also nicht =1 sondern =1000

p.p.
Diese Fassung der Datei ist verkürzt! - Also nochmal lesen, wenn die Quellen ausgepackt sind...

  • zurück zur Hauptseite: www.rcfriz.de