← 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