← ST-Computer 01 / 1987

Assemblerkurs Teil 2

Grundlagen

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