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