In diesem Teil des Assemblerkurses werde ich Ihnen die letzten Gruppen des 68000 Befehlssatzes vorstellen. Damit hätten wir die Adressierungsarten und den Befehlssatz abgeschlossen.
Zur Übersicht, gebe ich Ihnen an dieser Stelle, die Bezeichnungen der Gruppen, die in diesem letzten Teil des Kurses besprochen werden.
Mit dieser Gruppe von Befehlen werden Operanden um ein oder mehrere Bits nach links oder rechts verschoben. Dies kann in einer Reihe oder im Kreis erfolgen . Die verschiedenen Befehle unterscheiden sich nur durch die Verwendung der Bits, die aus der (ea) heraus- und hereingeschoben werden.
ASL
Arithmetisches Schieben links
ASR
Arithmetisches Schieben rechts
LSL
Logisches Schieben nach links
LSR
Logisches Schieben nach rechts
ROL
Rotation nach links
ROR
Rotation nach rechts
ROXL
Rotation mit X-Bit nach links
ROXR
Rotation mit X-Bit nach rechts
Beim arithmetischen Schieben des Operanden werden die Bits, die herausgeschoben werden, im C- und X-Flag gespeichert. Dem ASL-Befehl wird in die freigewordene Stelle immer eine Null nachgeschoben. Beim ASR-Befehl wird das höchstwertigste Bit kopiert, um damit die freiwerdende Stelle zu besetzen. Mit diesen Befehlen können Byte-, Wort- und Langwortdaten verarbeitet werden, nur wenn sich der Operand im Speicher befindet. Dann kann man nur mit Wortlänge arbeiten. Eine recht interessante Variante bietet der Befehl, mit einem Datenregister als Ziel. Hier ist es möglich über ein weiteres Datenregister oder einer Konstante, die Anzahl der Verschiebungen vorzugeben.
Beispiel:
C-Flag
D1 = %10011101 X
ASL.B #4,D1
D1 = %11010000 1
ASR.B #4,D1
D1 = %11111001 1
Wendet man beide Beispiele auf das Register D1 an, so erscheint, nach Ausführung der Befehle, das entsprechende Ergebnis (siehe D1 letzte Zeile). Die Konstante kann Werte zwischen eins und acht annehmen. Ein Schieben um eine Stelle nach links, entspricht einer Multiplikation mit zwei, (nach rechts, dementsprechend durch zwei). Das Vorzeichen der Zahl bleibt bei diesen Operationen erhalten. Findet durch das Schieben ein Vorzeichenwechsel statt, so wird dies in dem V-Flag vermerkt.
Bei diesen zwei Befehlen gibt es nur einen winzigen Unterschied, im Vergleich zu dem ASR-Befehl. Der ASL-Befehl ist identisch mit dem LSL-Befehl. Während beim ASR-Befehl das Vorzeichenbit dupliziert wird, wird beim LSR-Befehl einfach eine null nachgeschoben.
Das Bit, daß jeweils aus dem Operanden herausgeschoben wird, wird im C-Flag gespeichert und gleichzeitig an die freigewordene Stelle kopiert. Somit ist der Kreis gschlossen. Ebenfalls werden bei dieser Gruppe von Befehlen die Flags entsprechend gesetzt, wobei diese beiden Rotierbefehle das X-Flag nicht verändern. Als kleine Aufgabe: Wieviele Einsen sind in einem Datenregister enthalten?
Lösung:
MOVE.L #31,D3
CLR.L D2
LOOP ROR.L #1,D1
BCS L1
ADDQ #1,D2
L1 DBRA D3,LOOP
Der Schleifenzähler (D3) wird mit dem Wert 31 geladen, da der DBcc-Befehl bei -1 abbricht. Das Register D2 wird gelöscht, um anschließend die Anzahl der vorkommenden Einsen zu speichern. D1 selbst enthält die zu untersuchende Zahl.
Bei diesen Befehlen erfolgt das Rotieren über das X-Flag. Das X-Flag fungiert hier praktisch als ein Zwischenspeicher. Dabei wird das Bit, daß rausgeschoben wird, ins X-Flag geschoben. Dessen Inhalt wiederum, geht an den Anfang der Rotation, um die Kette zu schließen.
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.
Für den Platzhalter „d“ kann hier „L oder R“ eingesetzt werden. Um die Wirkung der Befehle besser klarzumachen, habe ich noch eine grafische Darstellung gewählt, an der Sie die Verhältnisse genau ersehen können.
Wie der Name schon sagt, kann man mit diesen Befehlen einzelne Bits manipulieren. Deshalb haben die Befehle auch keine Operandengröße. In welcher Art und Weise man die Bits manipulieren kann, sagt Ihnen die folgende Tabelle:
Steht der Zieloperand im Speicher, so kann nur ein Bit innerhalb eines Bytes angesprochen werden. Nur wenn ein Datenregister das Ziel der Operation ist, sind alle 32 Bit ansprechbar. Das niederwertigste Bit hat dabei die Nummer null.
Als erstes wird das spezifizierte Bit getestet. Ist es null, so wird das Z-Flag (Zero), das einzigste Bit im CCR, daß von dieser Gruppe verändert wird, auf eins gesetzt. Dies entspricht dem BTST-Befehl. Dieser Befehl wird häufig mit einem anschließenden Bcc-Befehl gebraucht. Durch diese Kombination kann man einfache Entscheidungen aufgrund eines Bits fällen. Die anderen drei Befehle bauen auf dem BTST-Befehl auf. Nachdem das entsprechende Bit getestet worden ist, wird das angesprochene Bit anschließend gesetzt (BSET), gelöscht (BCLR) oder geändert (BCHG). In der Tabelle der Adressierungsarten steht der BTST-Befehl stellvertretend für die anderen Befehle dieser Gruppe.
In dem zweiten Teil hatten Sie ja schon einen Teil dieser Gruppe kennengelernt. Dies war im Prinzip nur der leistungsstarke MOVE-Befehl. Die restlichen Befehle dieser Gruppe sind:
EXG
Austausch von Registerinhalten
LEA
Lade effektive Adresse
LINK
Baue Stackbereich auf
PEA
Lege Adresse auf Stack
SWAP
Vertausche Registerhälften
UNLK
Baue Stackbereich ab
Manchmal ist es notendig, die Inhalte von zwei Registern auszutauschen. Dazu dient dieser Befehl. Dieses Problem wird Ihnen in einer höheren Programmiersprache schon mal begegnet sein. Zur Lösung benötigten Sie eine weitere Variable. In Assembler existiert ein Befehl, der ohne ein weiteres Register zwei Registerinhalte austauscht. Der Befehl arbeitet nur mit Registern und mit einer Operandenlänge von 32 Bit. Manche Assembler, lassen deshalb die Schreibweise EXG.L zu, obwohl dies nicht notwendig wäre.
Beispiel:
EXG Dl,A2
vorher nachher
D1 18273645 77661254
A2 77661254 18273645
Im Gegensatz zum Austausch von Registerinhalten wird hier der Inhalt eines Datenregisters vertauscht. Dies geschieht, indem die Bits 16-31 nach 0-15, und die Bits 0-15 nach 16-31 kopiert werden. Die Operation erfolgt also nur in Wortlänge. Die Flags N und Z werden nach dem 32 Bit-Ergebnis gesetzt. Das heißt, daß Z gleich eins wird, wenn die Bits im Datenregister (Bits 0-31) null sind. Entsprechend geht demzufolge das Bit 31 ins N-Flag.
Beispiel:
SWAP D2
vorher nachher
D2 11112222 22221111
Syntax Flags .x Quelle Ziel
XNZVC
ASd.x Dx,Dy ***** B,W,L Dn Dn
ASd.x #Kons,Dn ***** B,W,L # Dn
ASd.x (ea) ***** W abs,ARI
LSd.x Dx,Dy ***** B,W,L Dn Dn
LSd.x #Kons,Dn ***** B,W,L # Dn
LSd.x (ea) ***** W abs,ARI
ROd.x Dx,Dy -**O* B,W,L Dn Dn
ROd.x #Kons,Dn -**O* B,W,L # Dn
ROd.x (ea) -**O* W abs,ARI
ROXd.x Dx,Dy ***O* B,W,L Dn Dn
ROXd.x #Kons,Dn ***O* B,W,L # Dn
ROXd.x (ea) ***O* W abs,ARI
BTST Dn,(ea) --*-- Dn Dn,ARI,abs
BTST #Konst,(ea) --*-- # Dn,ARI,abs
Mit diesem Befehl kann man sich die Arbeit etwas erleichtern und die Programme übersichtlicher gestalten. LEA berechnet eine Adresse und gibt sie an ein Adressregister weiter. Somit ist die Operandenlänge auf 32-Bit festgelegt. Die Berechnung der effektiven Adresse erfolgt genauso wie bei der Adressrechnung der einzelnen Adressierungsarten. Nehmen wir einmal an, wir wollten ein Tabellenelement im Speicher bearbeiten. Die Adresse des Tabellenelements wird durch die Adressierung 7(A1,D3.L) beschrieben. Ein Zugriff auf dieses Tabellenelement sähe dann so aus.
MOVE.L 7(A1,D3.L),D0
Benötigt man mehrere Zugriffe auf diese Speicherzelle, und will man mit dieser Adressierung noch weitere Tabellenteile damit verarbeiten, so wird dies recht lange dauern. Denn jedesmal muß die Adressrechnung durchgeführt werden. Besser ist es dann schon, eine Adressrechnung auszuführen und diese Adresse in einem Adressregister zu behalten. Z. B.:
LEA 7(A1,D3.L),A0
MOVE.L (A0),D0,(A0)
Vergleicht man die zwei Darstellungen, die das gleiche bewirken, so merkt man, daß LEA und MO VE dieselbe effektive Adresse berechnen. LEA bringt das Ergebnis dieser Rechnung nach A0 und der MOVE den Inhalt der Adresse nach D0.
Der PEA-Befehl ist recht einfach erklärt. Er berechnet genauso wie der LEA-Befehl eine effektive Adresse, nur gibt er diese nicht an ein Adressregister weiter, sondern legt diese auf den Stack (-(A7)). Man könnte PEA entsprechend so beschreiben:
LEA (ea),-(A7)
Da dieser Befehl aber nicht existiert, könnte man ihn durch folgende Sequenz ersetzen:
LEA (ea),A3
MOVE.L A3,-(A7)
Der Aufbau eines Stackbereiches erfolgt in drei Schritten.
Die Zahl, die auf den Stackpointer addiert wird, ist eine 16-Bit-Zahl im Zweierkomplement, die Vorzeichenrichtig auf 32-Bit erweitert wird. Damit hat man zwei Möglichkeiten einen neuen Stackbereich anzulegen. Entweder mit einer positiven oder negativen Zahl. Meist wird eine negative Zahl benutzt, um Platz für Daten zu schaffen. Die Länge dieses Bereiches entspricht der Adressdistanz. Uber diesen Datenbereich können Datenblöcke, variabel im Stack untergebracht, zwischen Elaupt- und Unterprogramm ausgetauscht werden. Die Funktion soll folgende Grafik erläutern. Ein Schema zur Nutzung dieses Befehls erfolgt in diesem kleinen Beispiel:
; Hauptprogramm
.
JSR UNTERPROGRAMM
.
; Unterprogramm
LINK A6,-$80
MOVEM.L D0-D7/A3,-(A7)
.
.
.
MOVEM.L (A7)+,D0-D7/A3
UNLK A6
RTS
Da das Adressregister die Adresse des alten Stack enthält, sollte man es auf dem neuen Stack abspeichern. Geht dieser Wert verloren, so kann der alte Wert dieses Registers nicht wiederhergestellt werden (was ja nicht unbedingt tragisch ist), aber der RTS-Befehl findet nicht mehr die richtige Rücksprungadresse ins Hauptprogramm!!! Somit ist das Programm rettungslos verloren.
Hier haben wir das Gegenstück zu dem vorhergehenden Befehl. Was damit passieren kann, haben Sie ja schon erfahren. Warum das so ist, werden Sie mit der Funktion dieses Befehls verstehen. Der Abbau des Stackbereichs erfolgt in zwei Schritten.
1.) lade Stackpointer mit alter Adresse. 2.) lade Adressregister mit Wert vom Stack.
Sie sehen, der Schritt zurück geht nur richtig, wenn der Stackpointer mit seinem alten Wert geladen wird.
Damit hätten wir die vorletzte Gruppe der Befehle vervollständigt. Nun folgt noch die Tabelle dieser Gruppe:
Syntax Flags .x Que1le Ziel
XNZVC
EXG (ea),(ea) ----- Dn, An Dn, An
LEA (ea),An ----- ARI/(An)+/-(An) An
LEA (ea),An ----- abs,PCR An
LINK An,Kons ----- An
PEA (ea) ----- ARI/(An)+/-(An)
PEA (ea) ----- abs,PCR
SWAP Dn -**OO Dn
UNLK An ----- An
Mit der Vervollständigung dieser Gruppe wird die Besprechung des 60000’er Befehlssatzes abgeschlossen sein. Einen Teil, Sie erinnern sich, hatte ich ja schon besprochen. Zu den besprochenen Befehlen gehörten z. B. die Verzweigungsbefehle. Nun wieder eine Übersicht über die Befehle.
CHK
Prüfe gegen Grenzen
NOP
Keine Operation
RESET
Rücksetzen der Peripherie
Scc
Setze nach Bedingung
STOP
Halte die Verarbeitung an
TAS
Prüfe und setze ein Bit
TRAPV
Exception mit Bedingung
Mit diesem Befehl kann man den Inhalt eines Datenregisters, bzw. die Bits 0-15, gegen Grenzen prüfen. Die Grenzen sind zum einen die Null, die nicht verändert werden kann, und zum anderen eine effektive Adresse. Bewegt sich der Inhalt des Datenregisters in diesen Grenzen, so wird die Verarbeitung mit dem nächsten Befehl fortgeführt. Ansonsten setzt der 68000 das N-Flag auf eins, wenn Dn kleiner null, oder N= 1, wenn Dn größer (ea) war. Danach wird in eine Ausnahmeverarbeitung (Exception) verzweigt. Als Vektor für diese Exception wird der Vektor Nummer 6 benutzt. Zu beachten wäre, daß nach Ausführung des Befehls alle Flags, außer dem X-Flag, Undefiniert sind.
Meist findet der Befehl seinen Einsatz in Compilern. Mit ihm kann sehr einfach geprüft werden, ob der Zugriff auf eine indizierte Variable oder ein Matrizenelement zulässig ist.
Dies ist wohl der einfachste Befehl des 68000, denn hier arbeitet er nicht! Garnicht wäre etwas übertrieben, sonst wäre der Befehl ja nicht implementiert worden. Daran kann man sehen, das Nichtstun auch seine Berechtigung hat. So zum Beispiel kann er dazu benutzt werden, Zeitschleifen abzugleichen. Denn Rechenzeit kostet der Befehl schon. Da er auch Speicherplatz benötigt, kann man mit ihm einen Speicherbereich im Programm belegen, um diesen Teil später mit richtigen Befehlen vom Programm aus zu beschreiben.
Läßt man ein Unterprogramm mit einem NOP-Befehl anfangen, so kann sich das Unterprogramm gegen einen zweiten Aufruf schützen, wenn es den NOP-Befehl einfach mit dem Code für RTS überschreibt. Jeder weitere Aufruf führt dann zur sofortigen Beendigung des Unterprogrammes. Ebenso ist es denkbar, daß ein weiteres Programm dieses Unterprogramm wieder freigibt. Damit hat man sich eine aufwendige Verwaltung erspart.
Da dies ein priviligierter Befehl ist, kann er nur im Supervisormodus ausgeführt werden. Er ermöglicht es, die an die Resetleitung des 68000 angeschlossene Peripherie zurückzusetzen. Dies macht er, indem er die Leitung für 124 Clockzyklen auf „Low“ (ca. null Volt) legt. Danach befinden sich alle angeschlossenen Bausteine im Zustand nach dem Einschalten.
Dieser Befehl setzt ein Byte (nur Byteverarbeitung) in Abhängigkeit einer Bedingung (cc). Die Bedingungen sind die gleichen wie bei dem DBcc-Befehl. Ist die Bedingung erfüllt, so wird das über die (ea) adressierte Byte mit Einsen gefüllt, also auf $FF gesetzt. Ansonsten wird es auf $00 gesetzt.
Beispiel:
SEQ D3
vorher nachher
Z=1 (Bed. erfüllt)
D3 XX 00
Z = 0 (Bed. nicht erfüllt)
D3 XX FF
Dies ist ebenfalls ein priviligierter Befehl. Wird er nicht im Supervisor-Modus ausgeführt, so geht der 68000 in eine Exception. Der Ausdruck hinter dem STOP-Befehl ist eine 16 Bit Zahl. Diese Zahl wird benutzt, um das Statusregister damit zu laden. Ist dies geschehen, so geht der 68000 in den Halt-Zustand. Der Prozessor nimmt seine Arbeit wieder auf, wenn:
Tritt ein sogenannter katastrophaler Fehler auf, der nicht behoben werden kann, so setzt man den Prozessor auf Halt. Über die Interruptmaske, können laufende Prozesse, die sich an den Prozessor wenden, gesperrt werden. In den Flags könnte man eine Nummer ablegen, die später Auskunft über den Stoppunkt gibt. Somit kann man dann auf den Fehler im System schließen.
Als erstes wird der 8-Bit lange Operand getestet, und danach die Flags entsprechend gesetzt. Dann wird vom Operand das 7. Bit gesetzt, egal ob dieses null oder eins war.
Beispiel:
TAS $1000
vorher nachher
1000 00 80
1000 87 87
Den TRAP-Befehl hatte ich ja schon besprochen. An den TRAPV-Befehl wird allerdings nur eine kleine Bedingung geknüpft, bevor dieser in die Exceptionbehandlung geht. Der Befehl fragt erst das V-Flag ab. Ist dieses gesetzt, so wird TRAP ausgeführt.
Da wir jetzt die 56 Befehle des 68000 Befehlssatzes besprochen haben, gebe ich Ihnen noch eine Tabelle der privi-ligierten Befehle. Um diese Befehle nutzen zu können, müssen Sie zuerst die TOS-Funktion SUPER aufrufen. Diese bringt Sie in den Supervisormodus.
Es gibt allerdings noch einen „Befehl“ den der 68000 verarbeitet. Allgemein wird er als ILLEGAL bezeichnet. Trifft der 68000 auf eine Bitfolge, die er nicht als Befehl interpretieren kann, so löst er eine Exception aus. Diese Exception hat die Vektornummer 4. Manche Assembler haben diesen „Befehl“ als ILLEGAL implementiert, obwohl Motorola keinen Assemblersyntax dafür vorgesehen hat.
Das Programm, daß ich Ihnen zum Abschluß dieses Assemblerkurses vorstellen möchte, soll Ihnen den Rahmen für die GEM-Programmierung in Assembler geben. In diesem Rahmen befindet sich ein recht bekanntes Programmbeispiel. Eine Alertbox! Im Prinzip geht es nicht um diese Alertbox, sondern um die Verwaltung der AES und VDI Variablen. Da wir uns auf der untersten Ebene der Programmiersprachen bewegen, ist die Verwaltung in Assembler etwas aufwendiger als zum Beispiel in C.
Die erste kleine Routine hat den Namen Setblock. Dies ist ein TOS Aufruf, wie Sie ihn schon kennen. Diese Routine gibt den nicht benötigten Speicherplatz an das Betriebssystem zurück. Dazu wird aus der Base Page (256 Bytes), die vom Betriebssystem zur Verfügung gestellt wird, die Länge des Programms berechnet. Außerdem wird der Userstackpointer auf das obere Ende des neuen Bereichs gesetzt. Den alten Stackpointer wird der Setblock-Routine zur Verwahrung mitge-geben. Diese Prozedur ist nötig, wenn ein weiteres Programm oder ein RSC-File in den Speicher geladen werden soll. Denn das Betriebssystem verwaltet ja den Speicher.
Mit einem kleinen JSR verzweigen wir in unser Programm. Dieses Programm wird zur ordnungsgemäßen Terminierung mit einem RTS abgeschlossen. Allerdings habe ich auch ein gewaltsames Verlassen des Programms eingebaut, nämlich mit einem Sprung nach END.
Nehmen wir einmal an das Programm wird ordnungsgemäß verlassen, so wird das Programm nach dem Unterprogrammaufruf fortgesetzt. An dieser Stelle findet man den TOS Aufruf zur Eingabe eines Zeichens. Dann folgt die Marke END, mit der anschließenden Funktion TERM, die das Programm beendet.
Da ich im Rahmen dieses Assemblerkurses nicht auf die GEM-Programmierung eingehen kann, werde ich mich hier auf die Parameterübergabe beschränken. Um diesen Ausführungen folgen zu können, sollten Sie sich schon ein wenig mit der GEM-Programmierung auskennen. Die Variablennamen halten sich weitgehend an die üblichen Bezeichnung.
Syntax Flags .x Quelle Ziel
XNZVC
CHK (ea),Dn -*UUU Alle/An Dn
NOP -----
RESET -----
Scc (ea) ----- Alle/PCR/abs(lang)
STOP #Kons ***** # (16 Bit)
TAS (ea) -**OO Alle/An/PCR
TRAPV #Kons ----- # (0-15)
Wenn Sie sich einmal das kleine Unterprogramm AES anschauen, so sehen Sie, daß der AES Aufruf ein Softwareinterrupt ist. Zu diesem Zwecke werden zwei Register initialisiert. Das Register D1 enthält eine Adresse und D0 die Geheimnummer von AES. Die Sache sieht bis jetzt noch ganz einfach aus. Wenn Sie das Programm weiter verfolgen, und zu der Stelle AESPB (am Anfang vom Datenbereich) kommen, stehen die Adressen von weiteren Marken. Dies sind die Adressen der Felder zur Übergabe der Variablen-Felder an AES. Diese Felder muß der Benutzer entsprechend seinen Wünschen mit Daten versorgen. Wenn Sie richtig gezählt haben, so macht dies sechs Felder. Jedes Feld hat eine bestimmte Bedeutung.
Dieses Feld enthält alle Informationen über die Art der Funktion, sowie die Größe der Eingabefelder. Es besteht aus fünf Worten (10 Bytes). Hier erfolgt auch die Rückmeldung über die Größe der Ausgabefelder. Das Feld ist folgendermaßen aufgebaut:
contrl
Befehlsnummer
sintin
Größe des intin-Feldes in Bytes
sintout
Größe des intout-Feldes in Bytes
saddrin
Anzahl der Adressen im addrin-Feld
saddrout
Anzahl der Adressen im addrout-Feld
Die Einträge contrl, sintin und saddrin müssen von Ihnen gemacht werden. Rückmeldungen über die Anzahl der Einträge in den Feldern, die an Sie gehen, geben die Variablen sintout und saddrout an.
Dieses Feld enthält einige Daten über die Applikaton. Mit diesem Feld werden Sie wahrscheinlich nie in Berührung kommen.
Die anderen Felder müßten Ihnen bekannt Vorkommen. Basic beispielsweise, stellt Ihnen diese Variablen zur Verfügung. Wenn Sie nun noch beachten, daß diese Felder Wortlänge haben, so kann nicht mehr viel schiefgehen. Definiert wurden die Felder mit 128 Worten. Wollen Sie größere Datenmengen auf einmal verarbeiten, so vergrößern Sie diese.
Wenn Sie sich den Zeiger VDIPB anschauen, und das Unterprogramm VDI, so sehen Sie nicht viel Neues. Der Zeiger enthält diesmal nur 5 Einträge. Das CONTRL-Feld wird hier ähnlich benutzt. Da VDI mehr Einträge im CONTRL-Feld benötigt (Insgesamt 12 Wörter), habe ich saddrout mit acht Wörter reserviert. Die Funktion des CONTRL-Feldes sieht nun folgendermaßen aus:
contrl
Befehlsnummer
contrl + 2
Anzahl der Koordinatenpaare im ptsin-Feld
contrl + 4
Anzahl der Koordinatenpaare im ptsout-Feld
contrl + 6
Anzahl der Wörter im intin-Feld
contrl + 8
Anzahl der Wörter im intout-Feld
contrl +10
Unterfunktion-Befehlsnummer
contrl +12
Anwendungskennziffer
Die Variablen contrl, contrl+ 2, 6, 10 und 2 werden dem VDI übergeben. Als Rückmeldung enthalten contrl + 4, 8 und evtl, contrl+12 einen Wert.
Die Felder intin und intout werden von beiden Funktionen gemeinsam benutzt.
Da dies die Verwaltung der Variablen war, können wir uns nun mit dem Hauptprogramm befassen. Das Hauptprogramm beginnt mit vier Aufrufen. Zwei davon sind AES. Diese Funktionen dienen zur Initialisierung der Applikation. Als erstes, wird die Applikation mit Applikation Init angemeldet. Danach benutzt man die Funktion Graf Handle, um von ihr die Variable grhandle zu erhalten. Diese Variable benötigen Sie für die VDI-Aufrufe.
Da wir den Bildschirm benutzen möchten, müssen wir beim ATARI dies durch ein Open Virtuell Workstation dem Betriebssystem mitteilen. Als Dank erhalten Sie dann insgesamt 57 Werte. Diese Werte enthalten z. B. Daten über das Auflösungsvermögen des Monitors, Linien- und Schrifttypen usw..
Und als letzte Funktion Clear Workstation, die ich aus Schönheitsgründen nicht benutzt habe, löscht einfach den Bildschirm.
Jetzt sind wir endlich mit der Initialisierung fertig, und können unseren Ideen freien Lauf lassen. Die Alertbox, ein beliebtes Beispiel, wird deshalb so gern benutzt, weil GEM die komplette Verwaltung dafür übernimmt. Dazu gehört das Zwischenspeichern des benötigten Bildschirmbereiches, die Aufbereitung des Aussehens der Box, nebst ihrer Größe, bis hin zur Restaurierung des Bildschirms. Als Antwort bekommt man eine Zahl in intout zurück, die die Nummer der gedrückten Taste enthält.
Diese Information bringe ich zur weiteren Auswertung ins Register DO. Wurde die Taste 1 betätigt, so endet das Programm mit einem gewaltsamen Abbruch. Taste 3 hingegen beendet das Programm normal. Hier kann man schön die Restaurierung des Bildschirmes erkennen, da erst noch eine weitere Taste gedrückt werden muß, bis sich das Desktop wieder aufbaut. Die Taste 2 quitiere ich mit einer neuen Alertbox. Diese Box hat als einzigsten Ausgang nur das brutale Ende zur Folge.
Ich hoffe, ich konnte Ihnen mit diesem Assemblerkurs die Assemblersprache ein wenig näher bringen. Wenn Sie sich in Assembler ein wenig eingearbeitet haben, so dürftes es Ihnen keine Probleme bereiten Beispiele oder Anwendungen, die in Assembler programmiert wurden, zu verstehen und für sich selbst zu nutzen. Sind Sie mit der Materie etwas besser vertraut, so sollte Ihnen die Anpassung solcher Programme zu Ihren eigenen Zwecken unproblematisch sein.
Sven Schüler
; Dieses Programm soll Ihnen die Anwendung der VDI und AES
; Routinen zeigen. Das Programm beinhaltet hauptsächlich
; die Verwaltung der Routinen. Diesen Teil sollten Sie sich
; auf Diskette abspeichern, um sich unnötige Tipparbeit bei
; weiteren Programmen zu sparen. Aus Schönheitsgründen habe
; ich die Funktion CLEAR WORKSTATION nicht benutzt.
move.l a7,a5 ; Stackpointer speichern
move.l #nstack,a7 : neuen Stack setzen
move.l 4(a5),a5 ;
move.l $c(a5),d0 ; Textsegment
add.l $14(a5),d0 ; Datensegment
add.l $1c(a5),d0 ; Blocksegment
add.l #$100, d0 ; base page
move.l d0,-(a7) ; Speicherplatzbedarf
move.l a5,-(a7) ; alter Stackpointer
move #0,-(a7) ; dummy
move #$4a,-(a7) ; TOS SETBLOCK
trap #1 ; Aufruf
adda #12,a7 ; Stackkorrektur
jsr main ; Hautprogramm
move #1,-(a7)
trap #1 ; wartet auf Taste
addq.l #2,a7
end
clr.l (a7)
trap #1 ; beendet Programm
;
aes ; AES Aufruf
move.l #aespb,d1 ; Pointer
move #$c8,d0 ; AES Nummer
trap #2 ; doit
rts
;
vdi ; VDI Aufruf
move.l #vdipb,d1 ; Pointer
move.l #$73,d0 ; VDI Nummer
trap #2 ; doit
rts
;
main ; Anfang der Initialisierung
clr.l aplresv
clr.l ap2resv
clr.l ap3resv
clr.l ap4resv
move #10,opcode
clr sintin
move #1,sintout
clr saddrin
clr saddrout
jsr aes ; Applikation Init
;
move #77,opcode
clr sintin
move #5,sintout
clr saddrin
clr saddrout
jsr aes ; Graf Handle
;
move intout,grhandle ; grhandle von Routine speichern
;
move #100,opcode
clr contrl+2
move #11,contrl+6
move grhandle,contrl+12
;
lea intin,a1 ; Intin (0-9) löschen
move #9,d1
loop
move #1,(a1)+
dbra d1,loop
;
move #2,(a1)
jsr vdi ; Open virtuell Workstation
;
jmp nloesch ; Clear Workstation überspringen
;
move #3,contrl
clr contrl+2
clr contrl+6
move grhandle,contrl+12
move #1,intin
jsr vdi ; Clear Workstation
;
nloesch
;
; Hier kann nun ihr Programm folgen
;
move.l #alert,addrin ; Alarmtext nach addrin
box
move #52,contrl
move #1,contrl+2
move #1,contrl+4
move #1,contrl+6
move #0.contrl+8
move #1,intin ; 1. Knopf ist Returnknopf
jsr aes ; Form Alert
;
move intout,d0 ; Rückmeldung der Taste
cmp #1,d0
beq end ; Taste 1, dann ende
cmp #2,d0
bne weiter ; taste 2, weiter, mit Returntaste beenden
move.l #alert2,addrin ; 2. Alarmtext nach addrin
jmp box ; und ausführen
weiter
;
rts ; ende von main
.data
.even
; Daten zu den Alertboxen
alert
; Pictogramm (0-3)
dc.b "[1]"
; Text 5 Zeile a 40 Zeichen durch | getrennt
dc.b "[Dies ist eine|Alertbox in Assembler]"
; Bis zu drei Tasten. Pro Taste maximal 20 Zeichen
dc.b "[Taste 1|Taste 2|Taste 3]",0,0
.even ; Wichtig, da ungerade Adresse wahrscheinlich
;
alert2 ; dito
dc.b "[2]"+
dc.b "[Sie haben die Taste 2|gedrückt] "
dc.b "[ende]",0,0
.even
; Hier endet Ihr Programm
;
; Daten zu der Initialisierung
;
aespb ; Pointer auf AES Felder
dc.1 contrl,global,intin,intout,addrin,addrout
;
vdipb ; Pointer auf VDI Felder
dc.l contrl,intin,ptsin,intout,ptsout
;
grhandle ds.w 1 ; Speicher Graf Handle
; FELDER
contrl
opcode ds.w 1
sintin ds.w 1
sintout ds.w 1
saddrin ds.w 1
saddrout ds.w 8 ; noch 7 Worte für AES
;
global
apversion ds.w 1
apcount ds.w 1
apid ds.w 1
apprivate ds.1 1
aptree ds.l 1
ap1resv ds.l 1
ap2resv ds.l 1
ap3resv ds.l 1
ap4resv ds.l 1
;
intin ds.w 128
;
ptsin ds.w 128
;
intout ds.w 128
;
ptsout ds.w 128
;
addrin ds.w 128
;
addrout ds.w 128
;
.bss
.even
ds.l 300 ; 1200 Bytes müssten reichen
nstack ; Neuer Stackpointer