Assembler-Kurs Teil 9
Multipilkation und Division ---------------------------
Die Addition und Subtraktion von ganzen Zahlen mit ADC und SBC war noch recht einfach. Leider gibt es aber keine Assmblerbefehle wie MULT oder DIV, die unsere Berechnungen übernehmen könnten. Wenn wir eine Multiplikation durchführen wollen, dann müßten wir eigentlich ein Programm dafür schreiben, was allerdings eine ziemliche Quälerei wäre, da wir das Problem über Additionen lösen müßten. Glücklicherweise gibt es auch für dieses Problem eine geeignete ROM-Routine ($B357). Der Faktor muß dafür in den Speicherstellen $28 und $29, der zweite Faktor in $71/$72 bereitgestellt werden. Das Ergebnis dieser 16-Bit-Multi- plikation erhalten wir im X- (nieder- wertiges Byte) und Y-Register (höher- wertiges Byte). Dummerweise existiert keine entsprech- ende Routine für die Division. Hier tritt außerdem noch das Problem auf, daß beim Dividieren zweier ganzer Zahlen in den seltensten Fällen das Ergebnis eine ganze Zahl sein dürfte. Jetzt kommen wir um die gebrochenen Zahlen nicht mehr herum.
Die Flißkommazahlen (Floating Point,FLP) ----------------------------------------
Gebrochene Zahlen werden im allgemeinenn als Fließkommazahlen bezeichnet. Fließ- kommazahlen bestehen immer aus drei Teilen: der Mantisse, der Basis und dem Exponenten. Betrachten wir zunächst eine dezimale FLP-Zahl: 4500 ist darstellbar als 4.5 * 10↑3, was der Darstellung 4.5E3 ent- spricht (4.5 ist die Mantisse, 3 der Exponent und 10 die Basis). Die Zahl 0.045 ließe sich auch als 4.5 * 10↑-2 (4.5E-2) schreiben. beachten Sie bitte, daß beide Zahlen auf dieselbe Matissenform gebracht wurden und sich lediglich noch im Exponenten unterscheiden. Das haben wir durch ein Links- bzw. Rechtsverschieben des Dezimalpunktes erreicht. Bei dezimalen FLP-Zahlen ist das alles noch recht einfach, aber wie kann man binäre FLP-Zahlen in ein einheitliches Format bringen, so daß alle Zahlen dieselbe Anzahl von Bytes haben ? Betrachten wir das Problem anhand eines Beispiels: Die Dezimalzahl 50.125 soll in eine binäre FLP-Zahl umgewandelt werden. Die Umwandlung erfolgt in 5 Schritten: 1. Vorkommateil umwandeln Der Vorkommateil wird als Integerzahl behandelt und wie gewohnt umgerechnet
50 : 2 = 25 Rest: 0 niederw. Bit 25 : 2 = 12 Rest: 1 12 : 2 = 6 Rest: 0 6 : 2 = 3 Rest: 0 3 : 2 = 1 Rest: 1 1 : 2 = 0 Rest: 1
Das Ergebnis : 110010 2. Nachkommateil umwandeln Die Stellen nach dem Binärpunkt haben die Wertigkeiten 2↑-1, 2↑-2 usw. Bei der Berechnung muß daher die Dezimalzahl immer mit 2 multipliziert werden, bei auftretenden Vorkomma- stellen wird das jeweilige Bit gesetzt.
0.125 * 2 = 0.25 Vorkommast.: 0 0.25 * 2 = 0.5 Vorkommast.: 0 0.5 * 2 = 1 Vorkommast.: 1 n.Bit
Das Ergebnis : .001 3. Normalisierung der Mantisse Unter Normalisierung versteht man das Anpassen der Mantisse an ein be- stimmtes Format. Der Binärpunkt muß soweit verschoben werden, bis er links genau neben der höchstwertigen binären 1 steht.
Vorherige Mantisse: 110010.001 Normalisierte Man.: 0.110010001 * 2↑6
In unserem Bespiel mußte der Binär- punkt um 6 Stellen nach links ver- schoben werden, was durch eine Multi- plikation mit 2↑6 ausgegelichen werden muß, damit das Ergebnis nicht verfälscht wird. Die Mantisse ist nun in der richtigen Form und wir haben auch unseren bi- nären Exponenten (69 gefunden. Die binäre Basis ist logischerweise 2. 4. Umwandlung des Exponenten Zu unserem Exponenten 6 wird nun noch die Zahl 128 hinzuaddiert, damit es möglich ist, auch negative Exponen- ten, die bei der Normalisierung der Mantisse entstehen können, darzu- stellen. Bei der Rückumrechnung (Binär -> Dezimal) ist daher daruaf zu achten, daß vom binären Exponenten 128 abzuziehen ist.
6 + 128 ------- 134 (binär : 10000110)
Ergebnis : Mantisse: 0.110010001 Exponent: 10000110 Nun muß nur noch festgelegt werden, in wievielen Bytes diese Zahl abge- speichert werden soll. 5. Floatingpoint-Zahlen des C64 Dieser Computer kennt zwei verschie- dene Fließkommaformate, die sich hauptsächlich durch die Art der Spei- cherung des Vorzeichens unterschei- den. Die eine wird nur innerhalb der beiden Fließkommakkumulatoren verwen- det, die andere beim Abspeichern der Ergebnisse im Arbeitsspeicher. a) In den beiden Fließkommaakkumulatoren (abgekürzt: FAC und ARG) werden alle Berechnungen von Fließkommazahlen mit Hilfe von ROM-Routinen durchgeführt. Der FAC belegt in der Zeropage den bereich von Speicherstelle $61 bis $66, ARG von $69 bis $6E. Welche Auf- gabe die einzelnen Bytes havben, können Sie der nachfolgenden Tabelle entnehmen.
FAC ARG -------------------- Exponent $61 $69 Mantisse 1 $62 $6A Mantisse 2 $63 $6B Mantisse 3 $64 $6C Mantisse 4 $65 $6D Vorzeichen $66 $6E
Schauen wir nun, wie die FLP-Zahl aus unserem Beispiel im FAC oder ARG abgelegt wird. Der Exponent erhält 8 Bits, während sich die Mantisse auf 32 Bits ausbreiten darf. Die normalisierte Mantisse wird aber ab dem Binärpunkt linksbündig in die zur Verfügung stehenden Bytes eingetragen. Ist die Mantisse länger als 4 Bytes, so wird der überschüssige Teil einfach weggelassen. So entstehen u.a. die heißgeliebten Rechenfehler in einem Computer. Zusätzlich gibt es noch ein Byte, das das Vorzeichen enthält. Von diesem Byte ist jedoch nur das Bit 7 von Bedeutung (0 -> positiv ; 1 -> negativ), die anderen Bits sind uninteressant. Format der Fließkommaakkumulatoren:
/--------\ |10000110| \--------/ Exponent /--------+--------+--------+--------\ |11001000|10000000|00000000|00000000| \--------+--------+--------+--------/ 1 2 3 4 M A N T I S S E /--------\ |0xxxxxxx| \--------/ Vorzeichen
In hexadezimaler Form ergibt das: 86 C8 80 00 00 00 Sicher halten Sie die benutzung eines ganzen Bytes für nur ein Vorzeichenbit für Verschwendung. In den Flißkomma- akkumulatoren stört das jedoch niemanden, da es ohnehin nur zwei davon gibt. Außerdem erleichtert das getrennte Vorzeichen die Berechung bei der Fließ- kommaarithmetik. Hier nun eine kleine Auswahl der möglichen Rechenoperationen:
/----------------------------+-------\ |Rechenoperation |Routine| +--------------+-------------+-------+ |Addition |FAC :=ARG+FAC| $B86A | |Subtraktion |FAC :=ARG-FAC| $B853 | |Multiplikation|FAC :=ARG*FAC| $BA2B | |Division |FAC :=ARG/FAC| $BB12 | |Potenzierung |FAC :=ARG↑FAC| $BF7B | \--------------+-------------+-------/
Vor dem Aufruf der Routinen müssen FAC und ARG natürlich die gewünschten Zahlen enthalten. Wie Sie sehen, enthält der FAC immer das Ergebnis der Berechnungn. Spontan fallen uns zwei Probleme auf: Wie bekomme ich die Zahlen in den FAC oder ARG und wie kann ich das Ergebnis abspeichern, damit es erhalten bleibt? Auch dafür stehen uns wieder einige ROM-Routinen zur Ver- fügung, die Sie gleich noch kennenlernen werden. Zunächst jedoch zurück zu dem zweiten Fließkomme-Format von demm die Rede war. b) Es handelt sich um das Format, in dem die Fließkommazahlen im Speicher abge- legt werden, entweder als Ergebnis einer Berechung, oder als Operanden für die Rechenoperationen in FAC und ARG. Ein Format, das sich auf den Arbeitsspeicher bezieht, sollte möglichst kurz und speicherplatzsparend sein. Ein eigenes Byte für ein Vorzeichen ist jetzt völlig ausgeschlossen. Die ganze Fließkommazehl wird nun durch einen kleinen Trick auf 5 Bytes beschränkt. Wir wissen, daß rechts neben dem Binär- punkt der normalisierten Mantisse eine 1 steht (deshalb haben wir den Punkt ja dorthin gesetzt). Wenn uns sowieso bekannt ist, daß es sich um eine 1 handelt, dann brauchen wir sie doch nicht mehr mit abzuspeichern. Diese 1 muß lediglich bei allen Berechnungen berücksichtigt werden. Diese Bits nennt man Hidden Bits (versteckte Bits). Da das äußerst links stehende Bit der Mantisse nun frei geworden ist, können wir dort unser Vorzeichen unterbringen. Tatsächlich benötigen wir jetzt nur noch 5 Bytes für die Zahlendarstellung. Format im Arbeitsspeicher:
/--------\ |10000110| \--------/ Exponent /Vorzeichen-Bit /+-------+--------+--------+--------\ |01001000|10000000|00000000|00000000| \--------+--------+--------+--------/ 1 2 3 4 M A N T I S S E
Hexadezimal erhält man: 86 48 80 00 00 ROM-Routinen zur Handhabung der Fließ- kommazahlen Innerhalb dises Kursteiles kann ich Ihnen leider nur eine kleine Auswahl dieser ROM-Routinen vorstellen.
$B3A2|Wandelt eine vorzeichenlose ganze |Zahl (0...255), die im Y-Register |steht, in eine FLP-Zahl im FAC um. | $BBA2|Lädt FAC mit einer FLP-Zahl aus |dem Arbeitsspeicher, auf die der |Zeiger Akku/Y-Register weist und |wandelt sie in das benötigte 6- |Byte-Format um. | $BA8C|Lädt ARG mit einer FLP-Zahl aus |dem Arbeitsspeicher, auf die der |Zeiger Akku/Y-Register weist und |wandelt sie in das benötigt 6- |Byte-Format um. | $BC0C|Kopiert den Inhalt des FAC in den |ARG. | $BBFC|Kopiert den Inhalt des ARG in den |FAC. -----+---------------------------------- $B7F7|Wandelt den Inhalt des FAC in eine |vorzeichenlose Integerzahl (0...
|65535) in den Speicherzellen $14/ |$15 und Y-Register/Akkumulator um. | $BDDD|Wandelt den Inhalt von FAC in eine |ASCII-Zeichenkette und legt sie im |Puffer am $0100 ab. | $BBD4|Wandelt den FAC (Rechenergebnis) |in eine 5-Byte-FLP-Zahl und legt |sie im Speicher under der Adresse |ab, die der Zeiger X-Register/Y- |Register bildet. Die USR-Funktion (User callable machine language SubRoutine) Bisher waren wir es gewohnt, unsere ASSEMBLER-Programme von BASIC aus mit SYS-Befehlen aufzurufen. Die Parameter- übergabe konnte nur durhc das POKEn der Werte an die jeweilige Adresse erfolgen. Wenn der zu übergebende Wert jedoch eine FLP-Zahl ist, dann müßte die Zahl in BASIC zerlegt werden, um sie dann stück- weise in den FAC zu POKEn. Das wäre doch sehr umständlich. Speziell für das Bearbeiten von Fließkommazahlen gibt es daher die USR-Funktion. Der Vorteil dieser Funktion ist, daß der Inhalt der angegebenen Variablen automatisch in den FAC übertragen wird. Aber woher weiß der Computer , wo unsere ASSEMBLER-Routine beginnt? Bei der Ausführung von USR wird der indirekte Sprung JMP ($0311) ausge- führt. Die Einsprungadresse des ASSEMBLER-Programms muß als Zeiger in den Adressen $0311 (niederwertiges Byte) und $0312 (höherwertiges Byte) vor dem Aufruf bereitgestellt werden. Unter anderem enthält das Programm "ASSEMBLER-KURS 9" auch ein Beispiel für die Benutzung von USR. Im nächsten (und letzten) Kursteil geht es dann um den Umgang mit den Interrupts. Mich interessiert sehr Ihre Meinung, wie Ihnen der Kurs gefallen hat. Welche Themen wurden zu kurz behandelt, wo sehen Sie noch Probleme, was fanden Sie besonders gut? Schreiben Sie mir doch bitte Ihre positive oder negative Kritik:
Ralf Trabhardt Philippsbergstraße 45 6200 Wiesbaden
Also, bis zum nächsten Kursteil...