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-( niederwertiges Byte) und Y-Register ( höherwertiges Byte) .
Dummerweise existiert keine entsprechende 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 .5 E3 entspricht (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 .5 E-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
Linksbzw. 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 Vorkommastellen 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 bestimmtes 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ärpunkt um 6 Stellen nach links verschoben werden, was durch eine Multiplikation 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 binä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 Exponenten, die bei der Normalisierung der
Mantisse entstehen können, darzustellen. 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 abgespeichert werden soll.
5 . Floatingpoint-Zahlen des C64 Dieser Computer kennt zwei verschiedene Fließkommaformate, die sich
hauptsächlich durch die Art der Speicherung des Vorzeichens unterscheiden. Die eine wird nur innerhalb der
beiden Fließkommakkumulatoren verwendet, 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 $6 E. Welche Aufgabe 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 C880000000
Sicher halten Sie die benutzung eines
ganzen Bytes für nur ein Vorzeichenbit
für Verschwendung. In den Flißkommaakkumulatoren 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 Verfü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 abgelegt 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ärpunkt 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:8648800000
ROM-Routinen zur Handhabung der Fließkommazahlen
Innerhalb dises Kursteiles kann ich
Ihnen leider nur eine kleine Auswahl
dieser ROM-Routinen vorstellen.
$ B3 A2| 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ückweise 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) ausgefü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.
Also, bis zum nächsten Kursteil... (rt/wk)