In diesem Teil des Assemblerkurses wollen wir die Adressierungsarten abschlieĂen. Daran anschlieĂend werden wir uns dem Befehlssatz des 68000 zuwenden.
3.) Absolute Adressierung
Mit dieser Adressierungsart hat man die Möglichkeit, direkt auf den Speicher zuzugreifen. Dazu existieren zwei Versionen:
- Absolut kurz
- Absolut lang
Diese beiden Adressierungsarten unterscheiden sich nur in ihrer LĂ€nge und Geschwindigkeit. AuĂerdem ist bei der Adressierungsart die GröĂe des Adressbereiches auf 64 K-Byte beschrĂ€nkt.
3.1) Absolut kurz
Die Schreibweise ist $XXXX oder $FFYYYY, wobei XXXX oder FFYYYY im Bereich von -32768 und +32767 liegt. Dies bedeutet, daĂ sich nur zwei Teile des Speichers ansprechen lassen. Der eine Teil liegt im unteren Teil des Speichers (0000 - 7FFF) und der andere im oberen (FF8000-FFFFFF). Jeder Teil ist jeweils 32 K-Byte groĂ. Der Adressraum zwischen diesen beiden Teilen kann nicht angesprochen werden.
Beispiel:
NEG.B $1000
vorher nachher
1000 12 EE
MOVE.W $1000,52000
vorher nachher
1000 5678 5678
2000 XXXX 5678
In dem ersten Beispiel wird der Inhalt der Speicherstelle $1000 negiert (Zweierkomplement). Wenn bei dieser Adressierungsart Byteverarbeitung vorliegt, so können auch ungerade Adressen angesprochen werden. Im zweiten Beispiel werden die Speicherstellen $1000 und $1001 nach $2000 und $2001 umgespeichert. Ein Zugriff auf eine ungerade Adresse mittels Wortverarbeitung, wie z. B. MOVE.W $1001,$2001, ist hier nicht möglich.
3.2) Absolut lang
Die Schreibweise ist $XXXXXXXX
Diese Adressierungsart erlaubt einem den Zugriff auf den gesamten Speicher. Allerdings ist die Schreibweise mit 32 Bit nicht erforderlich, da der 68000 nur 24 Adressierungen besitzt. Diese Schreibweise wĂŒrde einen Adressraum von 2 hoch 32, gleich 4 294 967 295, gleich 4 Gigabyte erlauben. Der wahre Adressraum betrĂ€gt aber nur 16 Megabyte. Ein weiterer Unterschied zu der kurzen Adressierung ist die Geschwindigkeit. Da der Prozessor einmal mehr auf den Speicher beim Lesen der Adresse zugreifen muĂ, ist diese Adressierungsart langsamer. Auch hier gilt: Zugriff auf ungerade Adressen nur mittels Byteverarbeitung.
4.) Adressregister indirekt (ARI)
Um die Effizienz in einigen Softwarebereichen zu erhöhen, wurde diese Adressierungsart mit verschiedenen Abwandlungen realisiert. Bei diesen Adressierungsarten enthÀlt ein Adressregister die Adresse der Daten.
Die Gruppe dieser Adressierungsart:
- Adressregister indirekt (ARI)
- ARI mit Postinkrement
- ARI mit Predekrement
- ARI mit Adressdistanz (Displacement)
- ARI mit Index und Adressdistanz
4.1) Adressregister indirekt
Die Schreibweise ist (An)
Mit der geklammerten Schreibweise ist immer der Inhalt der adressierten Adresse gemeint. Damit hat man den Zugriff auf den ganzen Speicher mit den Adressregistern.
Beispiel:
MOVE.W (A1),D3
vorher nachher
A1 00001000 00001000
D3 xxxxxxxx XXXX1234
1000 1234 1234
Bringt den Inhalt der Adresse, die im Adressregister A1 steht, in das Datenregister D3. Das bedeutet, weil in A1 $1000 steht, wird der Inhalt der Adresse $1000 nach D3 gebracht. Der Inhalt von A1, $1000, und dem höherwertigen Wort von D3 bleiben unverÀndert.
Die Anwendung dieser Adressierungsart liegt z. B. in der Abarbeitung von Sprungtabellen mittels eines JMP (An).
4.2) ARI mit Postinkrement
Die Schreibweise ist (An) +
Die Wirkung dieser Adressierungsart ist im Prinzip genauso wie bei der Adressregister indirekt. Nachdem die Adresse ĂŒber das Adressregister An angesprochen wurde, wird diese erhöht. Um wieviel es erhöht wird, hĂ€ngt von der Verarbeitung ab. Ist es Byteverarbeitung, so wird es um 1 erhöht. Wortverarbeitung ergibt eine Erhöhung um 2 und Langwortverarbeitung eine um 4.
Beispiel:
MOVE.W #$1234,(A3)+
vorher nachher
A3 000010000 00001002
1000 XXXX 1234
Dieses Beispiel zeigt das Abspeichern einer Konstanten nach einer Adresse, die im Adressregister A3 angegeben ist. AnschlieĂend wird dieses um 2 erhöht, da Wortverarbeitung vorliegt.
Eine Anwendung findet diese Adressierungsart zur Nachbildung von Stackpointern, zum Abarbeiten von Tabellen und um Daten zu verschieben (Blockmoves).
4.3) ARI mit Predekrement
Die Schreibweise ist -(An)
Wenn man die letzten zwei Beispiele verstanden hat, so werden Sie anhand dieser Schreibweise schon die Funktion erkennen.
Beispiel:
MOVE.L D7,-(A5)
vorher nachher
A5 00001004 00001000
D7 12345678 12345678
1000 XXXX 1234
1002 XXXX 5678
Um Ihnen hier noch einmal die Darstellung des Speichers zu erlĂ€utern, habe ich ihn in gerade Adressen aufgeteilt. Jede dieser Adressen ist gemaĂt der Datenbusbreite 16 Bit lang. Die höherwertigen 8 Bit sind in den geraden Adressen und die Niederwertigen in den Ungeraden.
Als erstes wird bei einem ARI mit Predekrement das entsprechende Adressregister, um die Zahl der Bytes die verarbeitet werden sollen, erniedrigt. An dieser Adresse kann dann das Datenregister abgespeichert werden.
Auch hier gelten die gleichen Anwendungsgebiete wie unter 4.2 beschrieben. Hier an dieser Stelle möchte ich noch eine wichtige Anmerkung machen. Wird bei Byteverarbeitung das Register A7 bzw. SP angesprochen, so wird um 2 in- bzw. dekrementiert, um den SP auf geraden Adressen zu halten!
4.4) ARI mit Adressdistanz
Die Schreibweise ist d16(An)
Die Angabe dl6 steht fĂŒr ein Displacement (Adressdistanz) von 16 Bit. Diese 16 Bit Zahl ist vorzeichenbehaftet (Zweierkomplement). Mit dieser Adressierungsart hat man die Möglichkeit, den Adresszeiger um einen konstanten Wert zu versetzen.
Beispiel:
MOVE.W $20(A2),$6000
A2 000040000 00004000
4020 8765 8765
6000 XXXX 8765
Adressrechnung:
Adressregister An $00004000
Displacement d16 $00000020
EA $00004020
Mit dieser Adressierungsart wird zum erstenmal eine etwas aufwendigere Adressrechnung notwendig. Wie Sie sehen, wird der Inhalt des Adressregisters, mit dem Displacement einfach addiert. Dies ergibt die Adresse, deren Inhalt nach $6000 gespeichert wird.
Die LeistungsfÀhigkeit einer solchen Adressierung zeigt sich in der Vielfalt der Anwendungsmöglichkeiten. Man ist damit in der Lage, z. B. einen Peripheriebaustein mit Werten aus einer Tabelle zu versorgen, wobei die Adressdistanz die verschiedensten Anwendungen ermöglicht. Oder man will, mittels eines Zeigers, auf zwei oder mehrere Tabellen zugreifen, usw...
4.5) ARI mit Index und Adressdistanz
Die Schreibweise ist d8(An,Rx.X)
Diesmal darf das Displacement nur 8 Bit lang sein. Auch diese Zahl ist vorzeichenbehaftet. AuĂerdem kommt noch ein beliebiges Register (Index) mit einer Angabe der VerarbeitungslĂ€nge hinzu. Der Index ist ebenfalls vorzeichenbehaftet.
Beispiel:
MOVE.L $F0(A6,D3.W),$3000
vorher nachher
A6 00004000 00004000
D3 15423008 15423008
6FF8 ABCD4321 ABCD4321
3000 XXXXXXXX ABCD4321
Adressrechnung:
Adressregister An $00004000
Index Rx.W $00003008
Displacement d8 $FFFFFFF0
EA $00006FF8
Bei dieser Adressierungsart ist die Adressrechnung noch etwas aufwendiger. Hier sehen Sie auch den Umgang mit vorzeichenbehafteten Zahlen. Rechnet man mit ihnen, so muĂ man sie in ihrer Stellenzahl auf die Bitbreite (32 Bit) des Ergebnisses erweitern. Es gelten die Regeln zur Addition mit Zweierkomplementzahlen.
Oft wird man diese Adressierungsart in der Tabellenbearbeitung einsetzen, mit der man variabel auf mehrere Tabellen zugreifen möchte. Dabei zeigt das Adressregister auf den Tabellenanfang, das Indexregister auf den Wert und das Displacement auf die entsprechende Tabelle. Das Displacement kann, wenn es nicht benötigt wird, in der Assemblerschreibweise weggelassen werden. Der Assembler setzt dann als Displacement Null ein.
5.) ProgrammzÀhlerrelative Adressierung (PCR)
Dies ist eine Adressierungsart, mit der der Atari ST-Anwender normalerweise nicht in BerĂŒhrung kommt. Das Anwendungsgebiet dieser Adressierungsart ist ein System, daĂ ohne eine MMU arbeitet und das programmunabhĂ€ngig von der Position im Speicher laufen soll. Diese Art der Programmierung nennt man PIC (Position-Independent-Code). Gute Assembler bieten die Möglichkeit, normale absolute Programme als programmzĂ€hlerrelative Programme zu ĂŒbersetzen. Der Anwender braucht sich dadurch keine Gedanken ĂŒber die LauffĂ€higkeit zu machen.
Systeme oder Maschinen, die nicht die Voraussetzung lieferten, die Programme oder Programmodule einfach und schnell zu verwalten, benötigten vom Betriebssystem aus einen PIC-Programm. Diese Programme machten einem die Programmierung schwerer, aber die Verwaltung war schneller, da die Programme im Speicher nicht verschoben werden muĂten.
Wie schreibt man PIC?
Da man den Adressbereich des Programmes nicht kennt, wĂ€hrend man es schreibt, muĂ man auf die Verwendung von absoluten Adressen, die sich auf das Programm beziehen, verzichten. SĂ€mtliche Sprungbefehle und Adressrechnungen mĂŒssen daher immer auf den ProgrammzĂ€hler bezogen sein.
5.1) PCR mit Adressdistanz
Die Schreibweise ist d16(PC)
Den Trick, der bei dieser Adressierungsart angewendet wird, will ich Ihnen an einem kleinen Beispiel erlĂ€utern. Nehmen wir einmal an, wir stĂŒnden in einer StraĂe vor der Hausnummer 10. Drei HĂ€user weiter, in der Hausnummer 16, wohnt ein Freund von uns. Will nun ein anderer Freund wissen, wo mein Freund wohnt, sage ich ihm âHausnummer 16â. Mittlerweile aber haben die Leute von der Stadtverwaltung die Hausnummern Ă€ndern lassen. Daraus folgt, daĂ meine Freunde sich nicht treffen werden. HĂ€tte ich aber gesagt: âEr wohnt drei HĂ€user weiter als ichâ, so hĂ€tten sie sich gefunden, unabhĂ€ngig von der Hausnummer.
Beispiel:
MOVE.W $0A(PC),D6
vorher nachher
PC 00003000 00003004
D6 XXXXXXXX XXXXFEDC
300C FEDC FEDC
Adressrechnung:
Programmcounter PC $00003000
Plus 2 2
Displacement d16 $0000000A
EA $0000300C
Das Displacement ist vorzeichenbehaftet. Dadurch ist es möglich, Daten vor und nach dem Befehl anzusprechen. Dieser Offset errechnet sich aus der Differenz zwischen dem PC+ 2 und der Adresse. Egal, in welchen Speicherbereich man das Programm verschiebt, die Differenz bleibt immer dieselbe.
5.2) PCR mit Index und Adressdistanz
Die Schreibweise ist d8(PC,Rx.X)
Ebenso wie in 4.5 und den Informationen aus 5 ĂŒber die PCR-Programmierung, ist diese Adressierungsart zu erklĂ€ren.
Beispiel:
MOVE.W $FE(PC,A5),D3
vorher nachher
PC 00005000 00003004
A5 006A0000 006A0000
D3 XXXXXXXX 00112233
6A5000 00112233 00112233
Adressrechnung:
Programmcounter PC $00005000
Plus 2 2
Index Rx.L $006A0000
Displacement d8 $FFFFFFFE
EA $006A5000
Auch hier findet eine Adressrechnung durch Addition mit dem Programmcounter+2, und dem Index und Displacement statt, die ja bei der Addition vorzeichenrichtig auf 32 Bit erweitert worden sind.
Nun haben wir alle Adressierungsarten besprochen und können uns jetzt den Befehlen zuwenden. Dies ist wieder ein recht groĂer Block.
Der Befehlssatz des 68000
Um die insgesamt 56 Befehle des 68000 zu beschreiben und zu erlÀutern, werden wir die Befehle erst einmal in bestimmte Kategorien zusammenfassen. Die Einteilung der Gruppen erfolgt aufgrund ihrer Funktion. Dadurch ergeben sich die folgenden sieben Gruppen:
- Arithmetische Befehle
- BCD Befehle
- Logische Befehle
- Schiebe- und Rotier-Befehle
- Bitmanipulationsbefehle
- DatenĂŒbertragungsbefehle
- Programmsteuerbefehle
In der Beschreibung der Befehle werde ich Ihnen die Funktion und Wirkungsweise, die Assemblerschreibweise, die Beeinflussung der Flags, die GröĂe der Operanden, die zugelasenen Adressierungsarten und die Anwendungsmöglichkeiten aufzeigen.
Einige Befehle will ich Ihnen auĂerhalb der Reihe schon vorab erklĂ€ren, da sie fĂŒr die Programme in diesem Kurs von Bedeutung sind.
Der Move Befehl
Der Move Befehl ist wohl der am meisten und am vielseitigsten gebrauchte Befehl ĂŒberhaupt. Er gehört zu der Gruppe der DatenĂŒbertragungsbefehle. Diesen Befehl hatte ich im Teil 1 schon angesprochen, nun folgt aber die ausfĂŒhrliche Beschreibung in allen Varianten. Zu diesen Varianten gehört:
MOVE
MOVEA
MOVEQ
MOVEM
MOVEP
MOVE to CCR
MOVE to SR
MOVE fr SR
MOVE USP
Sie sehen, dieser eine Befehl hat viele verschiedene Aufgaben, die in der Schreibweise deutlich gemacht wird.
Da ich den Befehl in seiner Funktion schon teilweise erklĂ€rt habe, werde ich die Beschreibung des Befehls nur noch zusammenfassen. Der MOVE-Befehl ĂŒbernimmt alle Aufgaben der Datentransporte. Mit ihm lassen sich die Register laden, Daten vom Speicher holen oder dort speichern und Daten zwischen den Registern oder dem Speicher hin- und hertransportieren.-Der MOVEA-Befehl wird benutzt, wenn ein Adressregister das Ziel der Operation ist. Der MOVE-Befehl erlaubt fast alle Kombinationen der Adressierung, auĂer Adressregister direkt als Ziel. Dies ist in der exakten Schreibweise dem MOVEA-Befehl Vorbehalten.
Ebenso wie der MOVEA sind der MO-VEM und MOVEP spezielle Befehle. Der MOVEM (M = Memory) wird benutzt, um einen, mehrere oder alle Daten- und Adressregister des 68000 im Speicher abzulegen und wieder zu holen. Die Auswahl der Register erfolgt durch die Angabe des jeweiligen Registers, das durch ein / getrennt oder in einem Block durch einen - getrennt wird.
Beispiel:
MOVEM.W A7/A5/D3-D6,-(SP)
A7 00001100
10FE A7
10FD A5
10FB D6
10FA D5
10F8 D4
10F6 D3
Die Reihenfolge lĂ€Ăt sich nicht bestimmen. Sie ist immer D0-D7 und dann A0-A7. Die einzige Ausnahme ist die Adressierungsart ARI mit Predekrement als Ziel. So wird in unserem Beispiel erst A7, dann A5,D6,D5,D4 und D3 auf den Stack abgelegt. Genau in der anderen Reihenfolge.
Mit dem MOVEP-Befehl (P = Peripherie) kann man Daten zu oder von den alternierenden Adressen des Speichers oder der Peripherie gespeichert oder geholt werden. Der MOVEP-Befehl ĂŒbertrĂ€gt die Daten immer byteweise, dadurch ist es möglich, die 8-Bit Peripherie einfach zu versorgen. Die 8-Bit-Peripherie ist so organisiert, daĂ die Adressen der Peripherieregister im 16 Bit breiten Speicher immer um zwei differieren und somit immer an geraden oder ungeraden Adressen liegen. Der Befehl schreibt oder liest somit immer byteweise an den geraden oder ungeraden Adressen. Diese byteweise Ăbertragung lĂ€uft so, daĂ das höchstwertige Byte des Datenregisters zuerst ĂŒbertragen wird, und das niedrigstwertige zuletzt.
Beispiel:
MOVEP.W D3,5(A3)
A3 005000
D3 1234
5004 XX12
5006 XX34
Hier wird das Datenregister D3 an die mit ARI und Displacement adressierten Adresse geschrieben. Dabei sieht man deutlich das Schreiben an ungeraden Adressen, wobei die geraden Adressen unverÀndert bleiben, ebenso wie das Adressregister A3.
Die nĂ€chste Gruppe des MOVE-Befehls dient dazu, das Statusregister (SR) bzw. das Condition-Code-Register (CCR) zu lesen oder zu schreiben. Ebenso benötigt man einen Befehl, um im Supervisormodus an den Userstackpointer (USP) zu gelangen. Der MOVE to SR und der MOVE USP Befehl sind privilegierte Befehle. Dies bedeutet, sie dĂŒrfen nur im Supervisor Modus angewendet werden. Verletzt man diese Regel, so fĂŒhrt dies zu einer Ausnahmebehandlung (Exception).
Der MOVE to CCR arbeitet nur mit Wortdaten, kann aber nur die untersten fĂŒnf Bit des CCR verĂ€ndern. Er dient zur gezielten Vorbesetzung der Flags des CCR.
Beispiel:
MOVE.W #%11001,CCR
XNZVC (Flags)
Ebenso arbeitet der MOVE to SR Befehl, wobei er aber alle funktionalen Bits des SR verÀndern kann. Die restlichen, nicht benötigten Bits bleiben Null. In der lesenden Richtung arbeitet der MOVE from SR Befehl. Mit diesem Befehl kann ein Anwender den Status des Systems erfahren und entsprechend darauf reagieren.
Beispiel:
MOVE.W SR,D3
Es wird das Statusregister exakt nach D3 abgebildet, wobei nur das niederwertige Wort benutzt wird. Die restlichen oberen 16 Bits bleiben erhalten.
Mit dem MOVE USP kann der Anwenderstackpointer im Supervisormodus gerettet bzw. mit dem alten oder einem neuen Wert wieder an das User-Programm ĂŒbergeben werden. Nun bleibt als letztes nur noch der MOVEQ Befehl (Q = Quick = schnell). Mit ihm lassen sich die Datenregister schnell mit einem Startwert von -128 bis +127 belegen. Der Befehl arbeitet immer mit einer 8-Bit-Zweierkomplementzahl und grundsĂ€tzlich wie bei der Langwortverarbeitung. D. h., daĂ das entsprechende Datenregister vorzeichenrichtig erweitert wird.
Nun folgt die Zusammenstellung der erlaubten Verarbeitungsbreite, der Flags, die beeinfluĂt werden, der erlaubten Adressierungsarten und der Assemblerschreibweise. Die Zeichen unter den Flags bedeuten:
- bleibt unverÀndert
â
wird entsprechend gesetzt
U Undefiniert
0 wird auf Null gesetzt
Bei der Schreibweise der erlaubten Adressierungsarten, bedeutet:
ARI alle Adressregister indirekt
abs Absolut kurz und lang
PCR alle Programmcounter relativ
SR Statusregister
CCR Condition Code Register
USP User Stack Pointer
Um alle Adressierungsarten zu ermitteln, kann man jede Quelle mit jedem Ziel verknĂŒpfen.
Die nĂ€chsten Befehle, die ich Ihnen nun vorstellen möchte, gehören zu der Gruppe der Programmsteuer-Befehle. Diese Gruppe ist eine sehr wichtige, da mit ihnen der Ablauf des Programms gesteuert werden kann. Zu ihnen gehören die Sprung- oder Verzweigungs-Befehle. Diese teilen sich wieder in bedingte und unbedingte SprĂŒnge auf. Hierzu sind auch die Unterprogrammaufrufe und die Systemaufrufe hinzuzurechnen.
- Bcc
- DBcc
- BRA
- BSR
- JMP
- JSR
- RTE
- RTR
- RTS
- TRAP
JMP und BRA
Diese beiden Befehle dienen zum unbedingten Verzweigen in Programmen (Ă€hnlich dem Basic Befehl GOTO). Der Unterschied liegt in der Art des Sprunges. Der BRA (Branch) Befehl arbeitet mit einer 8 oder 16 Bit Adressdistanz (relativ). Dies bedeutet eine Sprungsweite von -128 bis +127 (8-Bit), oder -32767 bis 32768 (16-Bit) vom augenblicklichen Stand des ProgrammzĂ€hlers. Der JMP (Jump) Befehl arbeitet absolut. D. h. es wird direkt an die Adresse, die ĂŒber die EA adressiert worden ist, gesprungen. Man kann dadurch in dem gesamten Speicherraum herumspringen. Mittels der Adressierung der EA, wird der Befehl vielseitig. Es ist auch möglich, ĂŒber die programmzĂ€hlerrelative Programmierung, einen relativen Sprung zu machen.
Syntax Flags .X Quelle Ziel
XNZVC
MOVE.x (ea),(ea) -â
â
00 B,W,L Alle Dn,ARI,abs
MOVEA.x (ea),An ----- W,L Alle An
MOVEQ #Kons,Dn -â
â
00 B # Dn
MOVEM.x Rlist,(ea) ----- W,L Dn,An abs, ARI/(An) +
MOVEM.x (ea),Rlist ----- W,L abs,PCR,ARI/-(An) Dn,An
MOVEP.x Dn,d(An) ----- W,L Dn D(An)
MOVEP.x d(An),Dn ----- W,L d(An) Dn
MOVE (ea),CCR â
â
â
â
â
W Alle/An CCR
MOVE (ea),SR â
â
â
â
â
W Alle/An SR
MOVE SR,(ea) ----- W SR Dn,ARI,abs
MOVE USP,An ----- L USP An
MOVE An,USP ----- L An USP
Beispiel:
CLR.L D0
BRA Marke
Marke MOVE.W D0,D3
MOVEA.L Marke,A3
JMP (A3)
Marke MOVE.W D0,D3
Mit dem Branch-Befehl kann man das Programm an einer anderen Stelle fortsetzen. Ebenso wĂ€re dies mit Jump möglich gewesen, solange man keinen PIC schreiben muĂ. Das zweite Beispiel zeigt einen Sprung ĂŒber das Adressregister A3. Damit können Sie variabel in verschiedene Programmteile verzweigen, indem Sie das Register A3 mit der Adresse des Programmteils versorgen.
Bcc und DBcc
Das GegenstĂŒck zu den unbedingten Verzweigungen sind die bedingten Verzweigungen. Die Verzweigung ist vom CCR abhĂ€ngig. Welche Bedingung gĂŒltig ist, bestimmt folgende Tabelle. Aus dieser Tabelle brauchen Sie nur die entsprechende Mnemonik fĂŒr cc einzusetzen.
Das mit dem â
gekennzeichnete cc, F und T, gilt nur fĂŒr den DBcc Befehl. Manche Assembler erlauben auch DBRA anstatt DBF. Der Unterschied zwischen BGT und BHI, liegt in der Abfrage der Flags. Die mit einem K am Ende der Zeile markierten Bedingungen bedeuten, daĂ eine Abfrage fĂŒr die Zweierkomplementarithmetik vorgenommen wird.
Mnemonik |
Bedeutung |
CC |
Carryflag = 0 |
CS |
Carryflag = 1 |
EQ |
Gleich (Z=1) |
F â
|
Falsch,Nie erfĂŒllt |
GE |
GröĂer oder gleich K |
GT |
GröĂer K |
HI |
Höher |
LO |
Kleiner oder gleich K |
LS |
Niedriger oder identisch |
LT |
Kleiner K |
MI |
Negativ |
NE |
Ungleich |
PL |
Positiv |
T â
|
Wahr, immer erfĂŒllt |
VC |
Kein Ăberlauf K |
VS |
Ăberlauf K |
Die Wirkungsweise
Kommt das Programm an einen Bcc Befehl, so wird zuerst die Bedingung ĂŒberprĂŒft. Ist diese wahr, so wird an die Stelle verzweigt, die hinter dem Befehl angegeben ist. Dies ist, wie beim BRA Befehl, eine 8- oder 16-Bit Adressdistanz im Zweierkomplement. Ist die Bedingung nicht erfĂŒllt, so setzt das Programm seinen Verlauf mit dem nĂ€chsten Befehl fort.
Der DBcc Befehl prĂŒft ebenfalls als erstes die Bedingung. Ist diese Bedingung erfĂŒllt, so wird nicht wie beim Bcc Befehl verzeigt, sondern beim nĂ€chsten Befehl das Programm fortgesetzt. Ist die Bedingung nicht erfĂŒllt, so wird das angegebene Datenregister um eins erniedrigt. Ist der Inhalt dieses Datenregisters gleich -1, so geht das Programm zum nĂ€chsten Befehl. Ansonsten wird das Programm an dem genannten Label fortgesetzt. Diese interessante Variante wird benutzt, um eine Schleife aufzubauen, die nur mit einer bestimmten Anzahl von DurchlĂ€ufen abgearbeitet wird. AuĂerdem kann mit der Bedingung ein Abbruch aus der Schleife erzwungen werden.
Beispiel:
Marke Add.L #$FF,D3
.
.
.
CMP.L #$8000,D3
BLT Marke
.
Marke MOVE.W D3,D1
.
.
.
CMP.W #0,D1
DBEQ D3,Marke
In dem oberen Beispiel wird die Schleife so lange abgearbeitet, bis das Datenregister D3 noch kleiner als $8000 ist. Das untere Beispiel enthÀlt als Abbruchbedingungen:
1.) D1 = 0
2.) D3 = - 1
Ist eine dieser Bedingungen erfĂŒllt, so wird der nĂ€chste Befehl abgearbeitet. Bei dieser Konstruktion der Schleife muĂ man beachten, daĂ die Schleife einmal mehr als der Inhalt von D3 durchlaufen wird. Soll das nicht der Fall sein, so sollte man vor Eintritt in die Schleife das entsprechende Register um eins erniedrigen.
JSR und BSR
Diese beiden Befehle dienen dazu, Unterprogramme aufzurufen (BASIC = GOSUB). Die AusfĂŒhrung der Befehle unterscheidet sich von den Befehlen JMP und BRA nur in einer winzigen Kleinigkeit. Bevor der Sprung zu der angegebenen Stelle erfolgt, wird die Adresse des nĂ€chsten Befehls auf dem Stack abgelegt und der Stackpointer um vier (Langwort) erniedrigt. Mit dieser Adresse ist dann eine Fortsetzung des Programms an dieser Adresse möglich. Dazu gibt es einen Befehl mit verschiedenen Varianten, der den RĂŒcksprung bewirkt.
RTS, RTE und RTR
RTS (Return from Subroutine) beendet jedes Unterprogramm. Dieser Befehl holt sich vom Stack die abgelegte Adresse, erhöht diesen um vier und lĂ€dt den ProgrammzĂ€hler mit dieser Adresse. Dadurch wird das Programm an dieser Stelle vorgesetzt. Unterprogramme können beliebig ineinander geschachtelt werden. Benutzt man den Stack, um Daten abzulegen, so muĂ man ihn vor dem RĂŒcksprung bereinigen, damit die richtige Adresse vom Stack geholt werden kann.
RTE (Return from Subroutine) beendet jedes Unterprogramm. Dieser Befehl holt sich vom Stack die abgelegte Adresse, erhöht diesen um vier und lĂ€dt den ProgrammzĂ€hler mit dieser Adrese. Dadurch wird das Programm an dieser Stelle vorgesetzt. Unterprogramme können beliebig ineinander geschachtelt werden. Benutzt man den Stack, um Daten abzulegen, so muĂ man ihn vor dem RĂŒcksprung bereinigen, damit die richtige Adresse vom Stack geholt werden kann.
RTE (Return from Exception) ist ein privilegierter Befehl. Er dient zum RĂŒcksprung aus Interrupt-, Trap- und Exceptionbehandlungen. Bevor der RĂŒcksprung erfolgt, wird das Statusregister mit dem Wert geladen, der sich auf dem Stack befindet. Dieser muĂ also, am Anfang der Exception, als erstes auf den Stack gebracht werden.
Der RTR (Return mit Laden der Flags) Befehl funktioniert genauso wie der RTE Befehl. Allerdings wird nur das CCR behandelt. Da der RTR kein privilegierter Befehl ist, kann er auch als RĂŒcksprung aus normalen Unterprogrammen benutzt werden. Auch hier muĂ das SR auf den Stack abgelegt werden. Trifft das Programm auf ein RTR, so wird nur das CCR wiederhergestellt.
Beispiel:
BSR Marke
.
.
.
Marke MOVEM.L D1-D3/A5,-(A7)
.
.
MOVEM.L (A7)+,D1 - D3/A5
RTS
JSR Marke
.
Marke MOVE SR,-(A7)
.
.
RTR
Hier in den ersten Beispielen sieht man den Umgang mit Unterprogrammen. Um die Arbeit etwas zu vereinfachen, kann man bei Unterprogrammen eine âVersorgungâ und eine âEntsorgungâ definieren. Zum Beispiel benötigt ein Unterprogramm als Versorgung die Adresse eines Puffers in A1, einige Register, mit denen man arbeitet, und als Entsorgung das Register D0, das anzeigt, ob die Operation erfolgreich verlaufen ist. Da die Regisdter zum Arbeiten auch im Hauptprogramm verĂ€ndert sind, sollte man sie vorher abspeichern, und am Ende des Unterprogramms wieder laden, damit im Hauptprogramm die alten Werte wieder verfĂŒgbar sind. Sollen ebenfalls die Flags gerettet werden, so bietet sich die nĂ€chste Konstruktion an. Als erstes wird das SR auf den Stack gebracht, um danach freie Hand zu haben.
TRAP
Durch diesen Befehl wird eine Excep-tionsbehandlung ausgelöst. Dieser Be-tehl bewirkt, daà der Inhalt des PC und des SR auf den Stack gerettet werden. Danach wird der PC mit der Adresse geladen, die durch eine Vektornummer spezifiziert worden ist. Dies ist eine Nummer zwischen 0 und 15. Damit sind der Vektor und auch die Adresse festgelegt, auf die der Vektor zeigt, an dem die Exception beginnt. Die Vektoren liegen an den Adressen $80 bis SBF. Dies sind die Vektoren $20 bis $2F.
Zum Beispiel verzweigt der TRAP #1 zu der Adresse, die im Speicher an Adresse $84 (Langwort) steht.
Das Programm
Nun kommen wir zu dem Programm dieser Ausgabe. Dieses Programm soll Ihnen einige grundlegende Arbeiten der Programmierung zeigen. Zu diesen Arbeiten gehört das Planen der Unterprogramme sowie deren Aufbau. Durch die Verwendung der Unterprogramme wird das Programm kĂŒrzer und ĂŒbersichtlicher.
Das Programm soll zwei positive Dezimalzahlen addieren. Als erstes geben wir dazu eine Information aus (Einleitung). Dann soll die erste Zahl eingegeben werden. Dies machen wir, indem wir den Benutzer dazu auffordern. Ebenso geschieht dies mit der zweiten Zahl. Danach wird das Ergebnis, mit einem Text versehen, ausgegeben.
Die ersten beiden Texte auszugeben, dĂŒrfte Ihnen keine Schwierigkeiten bereiten. Danach muĂ man dem Benutzer die Eingabe ermöglichen. Dies erledigt eine Betriebssystemroutine (RLINE) fĂŒr uns. Da wir diese Routine noch einmal benötigen, gestalten wir sie als Unterprogramm. Als Parameter benötigt diese Routine die Adresse eines Puffers. Der Puffer ist folgendermaĂen aufgebaut: Das erste Byte des Puffers enthĂ€lt vor Aufruf die maximale Anzahl der Zeichen, die eingelesen werden sollen. In dem zweiten Byte erhĂ€lt man die tatsĂ€chliche Anzahl. Ab dem dritten Byte stehen die Zeichen. Die Eingabe wird durch die maximale LĂ€nge oder RETURN beendet, wobei RETURN nicht mit ĂŒbergeben wird.
Da die Funktion RLINE Zeichen einliest, muĂ man noch ĂŒberprĂŒfen, ob nur Zahlen eingegeben worden sind. Die Umrechnung einer ASCII-Zahl in eine BinĂ€rzahl, ist recht einfach (ASCII-$30 = Bin). Die stellenrichtige Zusammenfassung der BinĂ€rziffern erfolgt mittels dem Hornerschema (Siehe Ausgabe 9 âSo rechnen Computerâ). Die Testroutine wird, da sie zweimal benötigt wird, ebenfalls als Unterprogramm ausgefĂŒhrt. Sie muĂ, bei Auftreten eines Fehlers, dies entsprechend kenntlich machen. Erstens durch Ausgabe eines Textes, und zweitens in einem Register, damit das Programm darauf reagieren kann.
Die Addition der beiden Zahlen erfolgt binĂ€r, und zwar mit dem ADD Befehl. Die Umrechnung des Ergebnisses in Dezimalziffern geht in genau der umgekehrten Reihenfolge. Da der DIVU Befehl maximal ein 16 Bit Ergebnis liefert, muĂ, damit kein Fehler auftritt, der Puffer entsprechend lang sein. Daraus folgt: 16 Bit = 65 535; Multipliziert mit zehn gleich 655 350; dann durch zwei geteilt ergibt 327 675. Dies entspricht der gröĂten Zahl fĂŒr jede Eingabe. Da die Eingabe auf die Anzahl der Ziffern beschrĂ€nkt wird, folgt daraus: 5 Ziffern. Um dieses Programm leistungsfĂ€higer zu machen, mĂŒĂte man eine bessere Umwandlungsroutine entwickeln oder vor der Umwandlung auf die gröĂte Zahl testen.
NatĂŒrlich wĂ€re es auch möglich gewesen, die Eingabe in einer Zeile, mit einem -(- Zeichen zu trennen.
Dieses Programm wurde mit dem ST-Assembler geschrieben.
Sven SchĂŒler
Svntax Flags Marke,(ea),n
XNZVC
Bcc Marke ----- Offset 8 oder 16 Bit
DBcc
Dn,Marke ----- Offset 8 oder 16 Bit
BRA Marke ----- Offset 8 oder 16 Bit
BSR Marke ----- Offset 8 oder 16 Bit
JMP (ea) ----- (An),d(An),d(An,Rx),abs,PCR
JSR (ea) ----- (An),d(An),d(An,Rx),abs,PCR
RTE â
â
â
â
â
wird gesetzt
RTR â
â
â
â
â
wird gesetzt
RTS ------
TRAP ------ 0 bis 15
CD
; Additionsprogmm
; Definitionen
conin equ 1
pline equ 9
rline equ 10
; Programmstart
start ; BegrĂŒĂungstext ausgeben
move.l #btxt,-(a7)
move.w #pline,-(a7)
trap #1
addq.l #6,a7
einszahl ; zur Eingabe auffordern
move.l #bltxt,-(a7)
move.w #pline,-(a7)
trap #1
addq.l #6,a7
jsr eingabe ; maximal 5 Zeilchen einiesen
jsr test ; nur Zahlen?
cmp.1 #-1,d0 ; Fehler aufgetreten
beq einszahl ; noch mal zur Eingabe
jsr umrechnung ; richtig, dann umrechnen
move.l d0,d7 ; Zwischenspeichern
zweizahl ; Text ausgeben
move.l #b2txt,-(a7)
move.w #pline,-(a7)
trap #1
addq.l #6,a7
jsr eingabe ; maximal 5 Zeichen
jsr test ; nur Zahlen
cmp.l #-1,d0 ; Fehler aufgetreten?
beq zweizahl ; noch ein mal
jsr Umrechnung ; ansonsten umrechnen
add.l d0,d7 ; und dazuaddieren
move.l #austxt,-(a7) ; Ausgabetext ausgeben
move.w #pline,-(a7)
trap #1
addq.l #6,a7
jsr bindez ; d7 nach ASCII Dez und ausgeben
move.w #conin,-(a7) ; auf Taste warten
trap #1
addq.l #2,a7
clr.w -(a7) ; Ende des Hauptprogramms
trap #1
; Unterprogramme
eingabe ; feste Parameter
move.l #puffer,-(a7)
move.w #rline,-(a7)
trap #1
addq.l #6,a7
rts
test ; testet den Puffer auf unerlaubte Zeichen
; Ausgang: d0
movem.l a1/d1,-(a7) ; Register retten
move.b puffer+1,d1 ; Anzahl der tatsÀchlichen Zeichen
cmp.b #0,d1 ; Kein Zeichen?
beq fehler ; Fehlerbehandlung
move.l #puffer+2,a1 ; Adresse des ersten Zeichens
tloop
move.b (a1)+,d0 ; Zeichen holen
cmp.b #'0,d0 ; mit 0 vergleichen
blt fehler1 ; kleiner? dann Fehler
cmp.b #'9,d0 ; mit 9 vergleichen
bgt fehler1 ; gröĂer? dann Fehler
sub.b #1,d1 ; nÀchstes Zeichen
bne tloop ; noch welche, ansonsten weiter
movem.l (a7)+,a1/d1 ; Register wieder laden
rts ; zurĂŒck
umrechnung
; Ausgang: d0
movem.l a1/d1/d2,-(a7) ; Register speichern
move.l #puffer+2,a1 ; Adresse des ersten Zeichens
move.b puffer+1,d1 ; Anzahl der Zeichen
clr d0 ; Anfangswert =0
uloop
mulu #10,d0 ; mit 10 multiplizieren
move.b (a1)+,d2 ; Zeichen holen
and.b #$0f,d2 ; oberes nibble ausblenden (-$30)
add.l d2,d0 ; aufaddieren
sub.b #1,d1 ; nÀchstes Zeichen
bne uloop ; noch? ansonsten weiter
movem.l (a7)+,a1/d1/d2 ; Register wieder laden
rts ; ende
fehler ; ftxt ausgeben
move.l #ftxt,-(a7)
move.w #pline,-(a7)
trap #1
addq.l #6,a7
bra aus
fehler1 ; f1txt ausgeben
move.l #f1txt,-(a7)
move.w #pline,-(a7)
trap #1,a7
addq.l #6,a7
aus
move.l #-1,d0 ; Fehler aufgetreten setzen
movem.l (a7)+,a1/d1 ; Register retten
rts ; ende
bindez ; bin nach dez mit Ausgabe
; Eingang: d7
movem.l a2/d7,-(a7) ; Register retten
movea.l #puffer+7,a2 ; Pufferadresse fĂŒr letzten Zeichen
dloop
divu #10,d7 ; durch 10
swap d7 ; Rest ins Lowword
add.b #$30,d7 ; +$30 ASCII
move.b d7,-(a2) ; im Puffer ablegen
clr.w d7 ; Rest löschen
swap d7 ; wieder tauschen
cmp.w #0,d7 ; noch was da?
bne dloop ; dann weiter
ausgabe
move.l a2,-(a7) ; ab letztem Zeichen ausgeben
move.w #pline,-(a7)
trap #1
addq.l #6,a7
movem.l (a7)+,a2/d7 ; Register wieder laden
rts
.data ; Datenbereich
btxt
dc.b 27,"E",27,"e"
dc.b "Dieses Programm addiert zwei positive Zahlen miteinander"
dc.b 10,10,10,13,0
.even
b1txt
dc.b 10,13,"Bitte die erste Zahl",10,13,0
.even
b2txt
dc.b 10,13,"Bitte die Zweite Zahl",10,13.0
.even
ftxt
dc.b 10,13,"Bitte mindestens eine!!!",10,33,0
.even
f1txt
dc.b 10,13,"Bitte Zahlen!!!",10,13,0
.even
austxt
dc.b 10,13,"Das Ergebnis ist "
.even
puffer
dc.b 5,0
dc.b 0,0,0,0,0,0
.even
.BSS ; Ende des Datenbereichs