home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1998 #6
/
amigamamagazinepolishissue1998.iso
/
coders
/
assembler-kurs
/
listings
/
listing2a.s
< prev
next >
Wrap
Text File
|
1977-12-31
|
19KB
|
390 lines
; Listing2a.s - Dieses Programm registriert im Byte "Zaehler", wie
; oft die rechte Maustaste gedrückt wurde, oder, besser noch, wie lange
; sie gedrückt wurde, denn wenn sie gedrückt bleibt, zählt der Zähler
; trotzdem weiter, um auszusteigen linke Maustaste
Anfang:
btst #2,$dff016 ; POTINP - rechte Maustaste gedrückt?
beq.s Dazu ; Wenn ja, springe zu Dazu
btst #6,$bfe001 ; Linke Maustaste gedrückt?
bne.s Anfang ; Wenn nicht, springe zu Anfang zurück
rts ; Wenn hingegen ja, dann steige aus!
Dazu:
move.b Zaehler,$dff180 ; Gib den Wert von Zaehler in Farbe 0
; (COLOR0)
addq.b #1,Zaehler ; Inkrementiere Zaehler um 1 (zählt 1 dazu)
bra.s Anfang ; Springe (immer) zu Anfang zurück
Zaehler:
dc.b 0 ; Dieses Byte ist unser Zähler
END ; Mit END wird das Ende des Listings
; markiert, alles, was folgt, wird vom
; Assembler nicht mehre interpretiert
Bemerke: POTINP ist der Name des Registers $dff016. Der (großgeschriebene)
Name nach dem Strichpunkt bezieht sich immer auf ein $dffxxx-
Register.
In diesem Beispielprogramm erkennt man die Verwendung der Label sei es als
Kennzeichnung von Befehlen (bne.s Anfang, bra.s Anfang...), wie auch um
Bytes anzusprechen (addq.b #1,Zaehler). Es besteht kein Unterschied
zwischen der Label Anfang und der Label Zaehler: beides sind Label, also
NAMEN, DIE EINEN BESTIMMTEN PUNKT IM SPEICHER MARKIEREN, OB ES NUN EIN
BYTE ODER EINE REIHE VON BEFEHLEN ODER SONST ETWAS IST. DAMIT KANN MAN DIE
BEFEHLE UNTER DEM LABEL AUSFÜHREN ODER AUCH NUR DAS BYTE NACH DEM LABEL
VERÄNDERN. Ich habe bemerkt, daß es vielen Schwierigkeiten bereitet, sich
mit dieser Logik vertraut zu machen. Machen wir einige Beispiele um die
Rolle der Labels genauer zu verstehen: stellt euch vor, ihr habt einen
kleinen Schrebergarten, der eingezäunt ist, und in dessen Mitte verläuft
ein kleiner Weg. Nachdem ihr ihn umgegraben habt, beschließt ihr
Erdbeeren, Kohl, Rüben und Petersilie zu pflanzen, also teilt ihr ihn in
vier gleich große Rechtecke ein und streut den Samen aus. Um zu wissen, wo
das verschiedenen Grünzeug wächst, verwendet man meistens solche
Plastiktäfelchen mit einem Spitz unten dran, auf dem ein Foto der
Erdbeere/Kohl... ist. Man steckt sie dann in die Erde, ihr kennt die
Dinger doch..? Also pflanzen wir sie: Auf einem Täfelchen steht
ERDBEEREN:, auf dem anderen KOHL:, dem nächsten RUEBEN: und dem letzten
PETERSILIE:. Wir haben die Etiketten so in die Erde gesteckt, daß sie
jeweils den ANFANG des abgebildeten Gemüses markieren, und somit zugleich
das ENDE des vorherigen.
ERDBEEREN: KOHL: RUEBEN: PETERSILIE:
\/ \/ \/ \/
..................oooooooooooooooooo^^^^^^^^^^^^^^^^^^-_-_-_-_-_-_-_-_-_
Wenn wir die "...." als Erdbeeren ansehen, den Kohl als "oooo", die Rüben
als "^^^^" und die Petersilie als "-_-_", dann wird ein "BNE KOHL" soviel
bedeuten wie "GEHE ZUR ETIKETTE KOHL:", und nicht "renn´ mitten ins Beet
hinein", oder "gehe in Richtung KOHL:". Einzi und allein "GEHE ZUM
TÄFELCHEN MIT DER AUFSCHRIFT KOHL: UND FÜHRE DIE BEFEHLE, DIE FOLGEN,
AUS", in diesem Fall werden wir die "oooo" ausführen. Wenn das Label so
verwendet wird:
addq.b #1,RUEBEN
Da tun wir nichts anderes als einen Samen im ersten Byte nach der Etikette
dazuzufügen, es ändert aber nicht seine Funktion! Es bedeutet nicht
Inhalte oder andere komische Dinge!!! Es markiert immer einen Punkt im
Speicher, also des Listings, das in unserem Fall der Anfang der Rüben ist.
Probieren wir mal, ein MOVE.B ERDBEEREN,RUEBEN zu machen:
ERDBEEREN: KOHL: RUEBEN: PETERSILIE:
\/ \/ \/ \/
..................oooooooooooooooooo.^^^^^^^^^^^^^^^^^-_-_-_-_-_-_-_-_-_
| |
\------->--------->-------->------>
Wie ihr seht, ist ein ".", also das Byte nach ERDBEEREN:, ins Byte nach
RUEBEN: kopiert worden. Versuchen wir nun ein MOVE.W KOHL,ERDBEEREN
ERDBEEREN: KOHL: RUEBEN: PETERSILIE:
\/ \/ \/ \/
oo................oooooooooooooooooo^^^^^^^^^^^^^^^^^^-_-_-_-_-_-_-_-_-_
|| ||
\<----<----<-----/
Wir haben die ersten zwei "oo", die sich nach KOHL: befanden, in die
ersten zwei Bytes nahc ERDBEEREN: kopiert.
Wenn man einen Punkt zwischen zwei Labels lesen/scheiben möchte, dann
brauchtr man nur ein weiteres hinzufügen: um 4 Bytes Kohl mitten in die
Rüben zu geben, werden wir in der Mitte der Rüben ein neues Label namens
RUE2: pflanzen, und danach ein MOVE.L KOHL,RUE2
Vorher:
ERDBEEREN: KOHL: RUEBEN: RUE2: PETERSILIE:
\/ \/ \/ \/ \/
..................oooooooooooooooooo^^^^^^^^^^^^^^^^^^-_-_-_-_-_-_-_-_-_
Nachher:
ERDBEEREN: KOHL: RUEBEN: RUE2: PETERSILIE:
\/ \/ \/ \/ \/
..................oooooooooooooooooo^^^^^^^^oooo^^^^^^-_-_-_-_-_-_-_-_-_
|||| ||||
\\\\ ---->---->----->-- ////
Wir haben die ersten 4 Bytes von KOHL: in die ersten 4 Bytes nach RUE2:
kopiet. Das .L bedeutet eben 4 Bytes...
Es funktioniert auf die gleiche Art und Weise, als wenn man die realen
Adressen verwenden würde, wie schon in LEKTION1 erklärt (WORT
PEDAL>PORTAL), nur daß anstatt mit den Adressen, bei denen jeder Samen
eine eigene hat, mit Etiketten, also Labels, gearbeitet wird. Unter
Verwendung der Adressen:
ERDBEEREN: KOHL: RUEBEN: PETERSILIE:
\/ \/ \/ \/
..................oooooooooooooooooo^^^^^^^^^^^^^^^^^^-_-_-_-_-_-_-_-_-_
123456789012345678901234567890123456789012345678901234567890123456789012
111111111122222222223333333333444444444455555555556666666666777
Wenn man mit Adressen arbeitet, kann man 4 Bytes von jeder Stelle an jede
Stelle kopieren, z.B. von Stelle 25 zu Stelle 60: Move.L 25,60
ERDBEEREN: KOHL: RUEBEN: PETERSILIE:
\/ \/ \/ \/
..................oooooooooooooooooo^^^^^^^^^^^^^^^^^^-_-_-oooo_-_-_-_-_
123456789012345678901234567890123456789012345678901234567890123456789012
111111111122222222223333333333444444444455555555556666666666777
|||| ||||
\\\\ --->---->---->---->--->---> ////
Die gleiche Operation läßt sich aber auch durchführen, indem man ein Label
an Position 25 und eines an Position 60 gibt:
Label1: Label2:
\/ \/
..................oooooooooooooooooo^^^^^^^^^^^^^^^^^^-_-_-oooo_-_-_-_-_
|||| ||||
\\\\ --->---->---->---->--->---> ////
Wieso man Labels den Adressen vorgezogen hat? GANZ EINFACH! Wenn wir
Adressen verwendet hätten, und zwischen dem Kohl und den Rüben etwas
eingefügt hätten, dann wäre die Adresse nicht mehr 60 gewesen, aber irgend
eine andere Zahl, z.B. 80, und wir hätten alle Adressen ändern müßen,
indem wir sie "nach vorne" schieben, um das Stück reinzupassen. Mit den
Labels hingegen macht uns ein Stück mehr oder weniger dazwischen nichts
aus, da der Assembler erst beim Assemblieren die realen Adressen zuteilt.
Probiert dieses Programm auszuführen, das erste Mal ohne den Mausknopf zu
drücken, nur die linke Taste zum Aussteigen: das Byte ZAEHLER ist in
diesem Fall 0 geblieben, wie man einfach mit dem Befehl M sehen kann.
Dieser Befehl zeigt die effektiven Werte in den Speicherzellen byteweise
an. Aufgerufen wird er z.B. mit M $50000 oder M Label: wir werden M
Zaehler tippen, und 0 erhalten, gefolgt von anderen Zahlen, die die
folgenden Bytes darstellen, uns aber nicht interessieren. Um im Speicher
voran zu kommen, drückt öfters RETURN, um auszusteigen, ESC. Die Bytes
werden natürlich in Hexadezimal angezeigt. Assembliert von neuem mit A und
drückt diesmal einige Male die rechte Maustaste, bevor ihr aussteigt (mit
der linken Taste): wenn ihr jetzt M Zaehler eingebt, werdet ihr eine Zahl
verschieden von 0 erhalten, der der Anzahl der Zyklen entspricht, in denen
die Maustaste gedrückt war. Diese Zyklen werden vom Prozessor sehr schnell
ausgeführt, und auch wenn ihr nur einen Augenblick lang die rechte Taste
drückt, werdet ihr eine Zaahl größer als eins erhalten. Es ist zu
beachten, daß der Zähler ein Byte groß ist, also einen Maximalwert von
$FF, also 255 oder %11111111 erreichen kann, danach startet er wieder von
0 (wenn man mit dem dazuzählen fortfährt). $FF+1=0, $ff+2=1... Die
Evolution dieses Programmes gegenüber dem vorherigen ist die etwas
komplexere Struktur der bedingten Sprünge, und ich rate euch, ja nicht
fortzufahren, bis ihr das nicht verstanden habt! Weiters wird ein Byte als
Variable verwendet. Diese Byte, Zaehler genannt, wird nicht nur
beschrieben, sondern auch gelesen, um dessen Wert in das Register $dff180,
Color0, einzutragen. Jetzt beginnt man zu verstehen, wie es einem Programm
möglich ist, verschiedene Werte zu speichern, die von Nutzen sind, wie
etwa die Leben von Player1, seine energie, seine Punkte, etc. Die
Verwendung von Labels ist dem Programmierer nützlich, das Programm selbst
aber, einmal assembliert, wird nur eine Serie von Bytes werden, die der
68000er lesen kann. Sie werden als Befehle interpretiert, die sich auf
eine direkte Adresse beziehen: um das bestätigt zu bekommen, assembliert
das Programm und macht ein D Anfang... So wird das Programm sichtbat, wie
es in Wirklichkeit ist: an Stelle der Label finden wir die EFFEKTIVEN
Adressen. Die erste Zahlenkolonne links sind die realen Adressen, die wir
gerade lesen, die zweite Kolonne zeigt die Befehle in ihrer REALEN Form,
also eine reine Bytesequenz (z.B. wird die erste Zeile BTST #2,$dff016 im
Speicher so aussehen: 0839000200dff016, wobei $0839 BTST bedeutet, 0002
ist das #2, 00dff016 ist die angesprochene Adresse), die dritte Kolonne
ist die disassemblierte Form der Befehle. Das ist genau das Gegenteil des
Assemblierens: hier werden die Bytes in Befehle wie Add, Move u.ä.
umgewandelt. Um zu Beweisen, daß die Befehle ganz bestimmte Zahlenfolgen
werden, tauscht die erste Zeile folgendermaßen aus:
btst #2,$dff016 ; POTINP - rechte Maustaste gedrückt?
Ersetzen durch:
dc.l $08390002,$00dff016
oder:
dc.w $0839,$0002,$00df,$f016
oder:
dc.b $08,$39,$00,$02,$00,$df,$f0,$16
In allen Fällen ist das Resultat 0839000200dff016 im Speicher, was vom
68000 als "btst #2,$dff016" interpretiert wird, also "ist Bit 2 von
$dff016 Null?".
Wenn die Variabel ein Word anstatt ein Byte wäre, würde das Programm
folgendermaßen aussehen:
Anfang:
btst #2,$dff016 ; POTINP - rechte Maustaste gedrückt?
beq.s Dazu ; Wenn ja, springe zu Dazu
btst #6,$bfe001 ; Linke Maustaste gedrückt?
bne.s Anfang ; Wenn nicht, springe zu Anfang zurück
rts ; Wenn hingegen ja, dann steige aus!
Dazu:
move.w Zaehler,$dff180 ; Farbe 0 - Ein .W beim move verwenden!
; (COLOR0)
addq.w #1,Zaehler ; ADDQ.W statt ADDQ.b!!!
bra.s Anfang ; Springe (immer) zu Anfang zurück
Zaehler:
dc.w 0 ; Dieses Byte ist unser Zähler
Da der Zähler nun ein Word groß ist, kann er maximal 65535 enthalten, das
entspricht $FFFF oder %1111111111111111.
Wenn wir ein Longword als Zaehler verwenden würden, dann hätte $FFFFFFFF
Platz, also ein paar Miliarden, bevor er wieder auf 0 springen würde. Es
ist aber aufzupassen, denn das höchstwertigste Bit (also das 31. im Falle
eines Longword) wird als Vorzeichen verwendet: probiert ein ?0FFFFFFF, ihr
werdet 268 Millionen und ein paar zerquetschte erhalten, und in Binärform
sind die vier höchsten Bit (also die ersten vier nach dem %) auf 0. Die
höchste Zahl die man erreichen kann, ist $7FFFFFFF, oder binär:
;10987654321098765432109876543210 ; Bitzahl von 0 bis 31
%01111111111111111111111111111111
Das Bit Nr. 31 (was das zweiundreisigste wäre, es zählt aber auch di NULL)
ist auf 0, während alle anderen auf 1 sind. Wenn man nun ein ?$7FFFFFFF+1
macht, erhält man -2 Milliarden und Etwas, und destomehr man die Ziffer
erhöht, desto mehr nähert man sich der 0, in der Tat erhält man mit ?-1
$FFFFFFFF, mit $-2 = $FFFFFFE.
Dieses System des höchsten Bits, das als Vorzeichen wirkt, kann auch für
Bytes und Words gelten: ein MOVE.B #-1,$50000 kann auch als MOVE.B
#FF,$50000 geschrieben werden. Das größtmögliche Byte würde also
%01111111, also $7F-> 127, für das Word gilt das gleiche,
%0111111111111111 -> $7FFF, also 32767. Aber es hängt davon ab, wie man
das Programm schreibt, ob man nun die Zahlen als positive und negative
ansieht oder absolut.
Probiert, das Listing so abzuändern, daß Zaehler: ein Word wird, wie oben
beschrieben: ihr könnt die Editorfunktionen des ASMONE "AUSCHNEIDEN und
EINFÜGEN" verwenden. Damit könnt ihr ein Stück Text "ausschneiden" und
irgendwo anders "aufkleben", also "einfügen". Dafür verwendet die Tasten
Amiga_rechts+b um den Anfang des Blockes zu markieren, den ihr
ausschneiden wollt; in diesem Fall wählt den Teil am Anfang des
modifizierten Listings aus, gleich nach dem "...folgendermaßen aussehen:".
Positioniert den Cursor eben über der Label Anfang: und drückt
Amiga_rechts+b. Nun könnt ihr den Block auswählen, der in negativ
erscheinen wird, indme ihr einfach mit den Pfeiltasten rauf und runter
fahrt. Unter dem dc.w 0 angekommen, drückt Amiga+c, und das Stück Text,
das nun das Listing beinhaltet, wandert in den Speicher. Nun geht zum
Anfang des Listings, durch Pfeil_rauf+SHIFT, und drückt Amiga+i... Wie von
Zauberhand erscheint eine Kopie des Textes, den ihr vorhin ausgewählt
habt. Nun müßt ihr nur noch ein END unter dem dc.w 0 setzen, einige
Leerzeichen vom limken Rand entfernt, besser noch ein TAB, um das orginale
Listing auszuschließen, das ja noch den Zaehler: mit dem Byte enthält.
Assembliert und Jumpt (startet es...).
P.S: Ignoriert die Zahlen, die nach jeden "J" auftauchen, einfach.
Sie werden später erklärt.
Sofort werdet ihr einen Unterschied beim Aufleuchten des Bildschirmes
feststellen, wenn ihr die rechte Maustaste drückt; macht ein M Zaehler,
und kontrolliert den Inhalt. Jetzt ist es ein Word, also werden jetzt die
ersten zwei Zahlen gültig sein, also die ersten zwei Bytes. Wenn z.B. ein
00 30 erscheint, dann wird das bedeuten, daß das ADDQ.W #1,Zaehler $30 Mal
ausgeführt worden ist, also 48 Mal (in Dezimal). Bei einem 02 5e $25e mal,
das entspricht (?$25e) 606 Mal.
Wenn ihr keine Experten im "Ausschneiden und Einfügen" (Cut & Paste) seid,
dann übt ein bißchen, indem ihr Textteile von einem Punkt zum anderen
kopiert. Beachtet auch, daß wenn ein mit Amiga_rechts+b ausgewählter Text
anstatt mit Amiga_c mit Amiga_x traktiert wird, gelöscht wird, mit Amiga_i
aber irgendwo anders eingefügt werden kann. Ich versichere euch,
programmieren ist das reinste Ausschneiden und Einfügen, da dieser Trick
dir das Neuschreiben von ähnlichen Programmteilen erspart, die nur kopiert
und leicht verändert werden müßen.
Auch mit den Binärzahlen sollte man sich vertraut machen, denn auch viele
Hardware-Register sind BITMAPPED, d.h. jedes Bit hat eine Bedeutung. Hier
eine Tabelle, um den Unterschied klarzumachen:
Hexadezimal Binär Dezimal
0 %00000 0
1 %00001 1
2 %00010 2
3 %00011 3
4 %00100 4
5 %00101 5
6 %00110 6
7 %00111 7
8 %01000 8
9 %01001 9
$A %01010 10
$B %01011 11
$C %01100 12
$D %01101 13
$E %01110 14
$F %01111 15
$10 %10000 16
$11 %10001 17
$12 %10010 18
... ... ...
Wie ihr seht, folgt das Binärsystem einer einfachen Logik, die die Zahl
mit einsen füllt, bis sie zu einer 11, 111, 1111 etc. gekommen ist: nach
einem %011 kommt es zu einem %100, nach einem %0111 zu einem %1000, nach
einem %01111 zu einem %10000 usw. Ich erinnere euch, daß Hexzahlen von
einem $-Zeiche vorangegangen werden, die Binärzahlen von einem %. Die
Zahlen in Dezimalformat hingegen stehen ohne nicht vorne dran. Wenn wir 9
oder $9 schreiben, meinen wir immer 9, aber es besteht ein großer
Unterschied zwischen $10 und 10!!! Denn 10 ist 10, $10 in Hex ist 16 in
Dezimal! Einfacher ausgedrückt, nach dem 9 ändert ein $ mehr oder weiniger
alles. Man muß die Zahlen nicht im Kopf konvertieren können, dafür gibt´s
ja den "?"-Befehl im ASMONE, der das REsultat in jedem Format ausgibt,
Dezimal, Hexadezimal, Binär und ASCII, also in Form von Charaktern. Denn
Charakter wie "abcd.." sind nichts anderes als Bytes, die nach dem
ASCII-Standart zugewiesen sind. So ist ein "a"=$61, während ein "A" ein
$41 ist. Es ist leicht mit einem ?"a" oder einem ?$61 in der Kommandozeile
nachgewiesen.
Bemerke: Das .s am BNE bedeutet SHORT, also Kurz (equivalent zu .b), im
Gegenteil zu .w, wenn das Label "weit" weg ist. Probiert einfach immer ein
.s zu setzen, und wenn das Label nach dem beq oder bne... zu weit weg ist
(mehr als 127 Byte), dann korrigiert es der Assembler mit .w automatisch.
Um das zu testen, probiert folgende Änderung:
Anfang:
btst #2,$dff016 ; POTINP - rechte Maustaste gedrückt?
beq.s Dazu ; Wenn ja, springe zu Dazu
btst #6,$bfe001 ; Linke Maustaste gedrückt?
bne.s Anfang ; Wenn nicht, springe zu Anfang zurück
rts ; Wenn hingegen ja, dann steige aus!
dcb.b 200,0 ; Dieser Befehl wird später erklärt, hier
; werden einfach 200 bytes $00 im Speicher
; zwischen dem RTS und der Label Dazu: eingefügt,
; um die Distanz zu vergrößern.
Dazu:
move.b Zaehler,$dff180 ; Gib den Wert von Zaehler in Farbe 0
; (COLOR0)
addq.b #1,Zaehler ; Inkrementiere Zaehler um 1 (zählt 1 dazu)
bra.s Anfang ; Springe (immer) zu Anfang zurück
Zaehler:
dc.b 0 ; Dieses Byte ist unser Zähler
Beim Assemblieren werdet ihr bemerken, daß der Assembler ein FORCED TO
WORD SIZE meldet. Das bedeutet, er hat das bne.s zu einem bne.w gezwungen,
weil die aufgerufene Routine zu weit weg war. Ich rate euch, immer ein .s
nach einem BRS, BNE, BRA, BEQ und ähnlichem zu setzen, wenn es nicht
stimmt, korrigiert es ja der Assembler. Man kann auch immer ein .w setzen,
aber die .s sind schneller und verbrauchen weniger Bytes im Speicher. Um
das Konzept der Label nochmal aufzugreifen: das dcb.b 200,0 ist ein
Paradebeispiel dafür, wie nützlich die Label sind, die uns davor bewahrt
haben, die nachfolgenden Adressen alle neuzuschreiben, indem man sie alle
um 200 Bytes aufstocken hätte müßen.