Makroassembler AS V1.41r7
Benutzeranleitung
Stand August 1998
IBM, PPC403GA, OS/2 und PowerPC sind eingetragene Warenzeichen der IBM Corporation.
Intel, MCS-48, MCS-51, MCS-251, MCS-96, MCS-196 und MCS-296 sind eingetragene Warenzeichen der Intel Corp. .
Motorola und ColdFire sind eingetragene Warenzeichen von Motorola Inc. .
UNIX ist ein eingetragenes Warenzeichen der X/Open Company.
Microsoft, Windows und MS-DOS sind eingetragene Warenzeichen der Microsoft Corporation.
Alle anderen Warenzeichen, die nicht ausdrücklich in diesem Abschnitt genannt wurden und in diesem Handbuch verwendet werden, sind Eigentum der entsprechenden Eigentümer.
Dieses Dokument wurde mit dem LaTeX-Satzsystem unter den Betriebssystemen Digital Unix, Linux und OS/2 auf AMD K6- und DEC Alpha-Prozessoren angefertigt und formatiert.
Diese Anleitung wendet sich an Leute, die bereits in Assembler programmiert haben und sich darüber informieren möchten, wie man mit AS umgeht. Sie hat eher die Form eines Referenz- und nicht Benutzerhandbuches. Als solches macht sie weder den Versuch, die Sprache Assembler an sich zu erklären, noch erläutert sie die Architektur bestimmter Prozessoren. Im Literaturverzeichnis habe ich weiterführende Literatur aufgelistet, die bei der Implementation der einzelnen Codegeneratoren maßgebend war. Um Assembler von Grund auf zu lernen, kenne ich kein Buch; ich habe es im wesentlichen im ,,Trial and error''-Verfahren gelernt.
Bevor es in medias res geht, erst einmal der unvermeidliche Prolog:
AS in der vorliegenden Version gebe ich als ,,Public Domain'' weiter,
d.h. die Programm-und Overlaydatei sowie die dazugehörenden
Hilfsprogramme dürfen frei kopiert und benutzt werden. Es
existieren keine Planungen, AS in ein kommerzielles oder
Shareware-Programm umzuwandeln. Diese Erlaubnis gilt jedoch nur unter
der Voraussetzung, daß die Startmeldung der Programme ---
insbesondere die Copyrightmeldung --- nicht entfernt oder
überschrieben wird und für das Kopieren/Verschicken nicht
mehr als eine Aufwandsentschädigung (unter DM 20,-) verlangt
wird.
Auf Anfrage wird auch der Quellcode dieses Programmes ausgegeben.
Daraus abgeleitete oder darauf aufbauende Programme müssen unter
den gleichen Bedingungen weitergegeben werden wie dieses Programm.
Ich fordere ausdrücklich dazu auf, dieses Programm per Diskette
oder Mailbox/Netzwerk zu verbreiten!
Es mag sein, daß Sie dieses Programm als Beilage zu einem
kommerziellen Programm erhalten haben. Die für das kommerzielle
Programm geltenden Lizenzbedingungen beziehen sich jedoch auf keinen
Fall auf AS.
Sollte Ihnen der Assembler so gut gefallen, daß sie mir
unbedingt Geld dafür schicken wollen, so fordere ich Sie dazu
auf, den Betrag für Greenpeace zu spenden.
Ich habe mich bemüht, das Programm so fehlerfrei wie nur
irgendmöglich zu machen. Da es aber grundsätzlich keine
fehlerfreie Software gibt (die einzigen Leute, die keine Fehler
machen, liegen auf dem Friedhof!), übernehme ich keine Garantie
für die Funktion von AS in einer bestimmten Umgebung (Hard-oder
Software) oder Haftung für entstehende Schäden. Für
Hinweise auf Fehler bin ich selbstverständlich immer dankbar und
werde mich bemühen, sie zu korrigieren.
Um eine möglichst schnelle Fehlerdiagnose und -korrektur zu
ermöglichen, bitte ich, dem Fehlerbericht folgende Angaben
beizufügen:
Von Telefonanrufen bitte ich abzusehen. Erstens, weil sich die
komplizierten Zusammenhänge am Telefon nur äußerst
schwer erörten lassen, und zweitens ist die Telekom schon reich
genug...
Die neueste Version von AS (DOS,DPMI,OS/2) findet sich auf folgendem
FTP-Server:
Wer über keinen FTP-Zugang verfügt, kann den Assembler auch
von mir anfordern. Ich werde aber nur Anfragen beantworten, die zwei
Disketten (für 720K/1,2M-Format 4/3 Stück) und einen
passenden, frankierten Rückumschlag enthalten. KEIN Geld
schicken!!!
So. Nach diesem unvermeidlichen Vorwort können wir wohl beruhigt
zur eigentlichen Anleitung schreiten:
AS bietet im Gegensatz zu normalen Assemblern die Möglichkeit,
Code für völlig verschiedene Prozessoren zu erzeugen.
Momentan sind folgende Prozessorfamilien implementiert:
Der Grund für diese Flexibilität ist, daß AS eine
Vorgeschichte hat, die auch in der Versionsnummer deutlich wird: AS
ist als Erweiterung eines Makroassemblers für die
68000er-Familie entstanden. Auf besonderen Wunsch habe ich den
ursprünglichen Assembler um die Fähigkeit zur
Übersetzung von 8051-Mnemonics erweitert, und auf dem Weg
(Abstieg?!) vom 68000 zum 8051 sind eine Reihe anderer fast nebenbei
abgefallen...die restlichen Prozessoren wurden allesamt auf
Benutzeranfrage hin integriert. Zumindest beim
prozessorunabhängigen Kern kann man also getrost davon ausgehen,
daß er gut ausgetestet und von offensichtlichen Bugs frei ist.
Leider habe ich aber häufig mangels passender Hardware nicht die
Möglichkeit, einen neuen Codegenerator praktisch zu testen, so
daß bei Neuerungen Überraschungen nie ganz
auszuschließen sind. Das in Abschnitt 1.1 gesagte hat also schon seinen
Grund...
Diese Flexibilität bedingt ein etwas exotisches Codeformat,
für dessen Bearbeitung ich einige Tools beigelegt habe. Deren
Beschreibung findet sich in Abschnitt 6.
AS ist ein Makroassembler, d.h. dem Programmierer ist die
Möglichkeit gegeben, sich mittels Makros neue ,,Befehle'' zu
definieren. Zusätzlich beherrscht er die bedingte Assemblierung.
Labels in Makrorümpfen werden automatisch als lokal betrachtet.
Symbole können für den Assembler sowohl Integer-, String-
als auch Gleitkommawerte haben. Diese werden --- wie Zwischergebnisse
bei Formeln --- mit einer Breite von 32 Bit für Integerwerte,
80/64 Bit für Gleitkommawerte und 255 Zeichen für Strings
gespeichert. Für eine Reihe von Mikrokontrollern besteht die
Möglichkeit, durch Segmentbildung die Symbole bestimmten Klassen
zuzuordnen. Dem Assembler kann man auf diese Weise die --- begrenzte
--- Möglichkeit geben, Zugriffe in falsche Adreßräume
zu erkennen.
Der Assembler kennt keine expliziten Beschränkungen bzgl.
Verschachtelungstiefe von Includefiles oder Makros, eine Grenze
bildet lediglich die durch den Hauptspeicher beschränkte
Rekursionstiefe. Ebenso gibt es keine Grenze für die
Symbollänge, diese wird nur durch die maximale Zeilenlänge
begrenzt.
Ab Version 1.38 ist AS ein Mehrpass-Assembler. Dieser hochtrabende
Begriff bedeutet nicht mehr, als das die Anzahl der Durchgänge
durch die Quelltexte nicht mehr zwei sein muß. Sind keine
Vorwärtsreferenzen im Quellcode enthalten, so kommt AS mit einem
Durchgang aus. Stellt sich dagegen im zweiten Durchgang heraus,
daß ein Befehl mit einer kürzeren oder längeren
Kodierung benutzt werden muß, so wird ein dritter (vierter,
fünfter...) Durchgang eingelegt, um alle Symbolreferenzen
richtig zu stellen. Mehr steckt hinter dem Begriff ,,Multipass''
nicht...er wird im weiteren Verlauf dieser Anleitung deswegen auch
nicht mehr auftauchen.
Nach soviel Lobhudelei ein dicker Wermutstropfen: AS erzeugt keinen
linkfähigen Code. Eine Erweiterung um einen Linker wäre mit
erheblichem Aufwand verbunden und ist momentan nicht in Planung.
Zum Thema ,,Herausgabe von Sourcen'': Die Sourcen von AS sind nicht
in einer Form, die ein einfaches Verständnis ermöglicht (==
null Kommentare). Sourcen werde ich daher nur für den Fall
herausgeben, daß jemand wirklich damit etwas anfangen will
(z.B. AS auf einen anderen Rechner portieren) und das daraus
entstehende wiederum Public Domain wird. Insbesondere will ich
verhindern, daß jemand 5 Zeilen ändert (bevorzugt den
Copyrighteintrag) und das Ergebnis dann kommerziell als ,,sein''
Programm vertreibt.
Obwohl AS als ein reines DOS-Programm angefangen hat, stehen auch
eine Reihe von Versionen zur Verfügung, die etwas mehr als den
Real-Mode eines Intel-Prozessors ausnutzen können. Diese sind in
ihrer Benutzung soweit als möglich kompatibel gehalten zur
DOS-Version, es ergeben sich natürlich bisweilen Unterschiede in
der Installation und der Einbindung in die jeweilige
Betriebssystemumgebung. Abschnitte in dieser Anleitung, die nur
für eine bestimmte Version von AS gelten, sind mit einer
entsprechenden Randbemerkung (an diesem Absatz für die
DOS-Version) gekennzeichnet. Im einzelnen existieren die folgenden,
weiteren Versionen (die als getrennte Pakete distributiert werden):
Für den Fall, daß man bei der Übersetzung
großer, komplexer Programme Speicherplatzprobleme bekommt,
existiert eine DOS-Version, die mittels eines DOS-Extenders im
Protected Mode abläuft und so das komplette Extended Memory
eines ATs nutzen kann. Die Übersetzung wird durch den Extender
merklich langsamer, aber immerhin läuft es dann noch...
Für Freunde von IBM's Betriebssystem OS/2 gibt es eine native
OS/2-Version von AS. Diese ist zwar zur Zeit nur 16-bittig, aber man
erspart sich immerhin den Umweg über DOS-Boxen und hat auch
keine Probleme mehr mit längeren Dateinamen.
Den reinen PC-Bereich verläßt man mit der C-Version von
AS, die so gehalten wurde, daß sie auf einer möglichst
großen Zahl von UNIX-artigen Systemen (dazu zählt aber
auch OS/2 mit dem emx-Compiler) ohne großartige Verrenkungen
übersetzbar ist. Im Gegensatz zu den vorherigen Versionen (die
auf den auf Anfrage erhältlichen Pascal-Sourcen basieren) wird
die C-Version im Quellcode ausgeliefert, d.h. man muß sich
mittels eines Compilers selbst die Binaries erzeugen. Dies ist aber
(für mich) der eindeutig einfachere Weg, als ein Dutzend
Binaries für Maschinen vorzukompilieren, auf die ich auch nicht
immer Zugriff habe...
Wer die bisherige Aufzählung liest, wird feststellen, daß
das meistverkaufte Betriebssystem der Welt aus Redmont in dieser
Aufzählung fehlt. Wer mich persönlich kennt, weiß,
daß ich Windows (egal, ob 3.X, 95 oder NT) nicht
für das Ei des Kolumbus halte. Kurzgesagt, ich bin ein
,,Windows-Hasser''. Auch wenn eine große Zahl an Leuten diese
Einstellung für überholt bis lächerlich erachten und
mir jetzt vorhalten, ich würde hier einem großen Teil
potentieller Anwender AS vorenthalten, so werden sie sich doch damit
abfinden müssen: Ich treibe die Entwicklung an AS primär
weiter, weil sie mir Spaß macht; AS ist ein
nicht-kommerzielles Projekt und ich nehme mir deswegen die Freiheit,
nicht auf potentielle Marktanteile zu schielen. Ich suche mir die
Plattformen aus, auf denen das Programmieren mir Spaß
macht, und Programmieren unter Windows macht mir definitiv keinen
Spaß! Ich habe übrigens durchaus schon einmal
Windows-Programme schreiben müssen, es ist also nicht so,
daß ich hier ohne Erfahrung etwas daherreden würde. Sofern
irgendjemand AS in diese Richtung portieren will, werde ich mich ihm
nicht in den Weg stellen, über die Sourcen hinaus hat er aber
nicht viel Hilfe von mir zu erwarten (und muß sich selber mit
den Anfragen der Qualität herumschlagen, warum AS denn jetzt
nicht mehr läuft, nachdem man den Brummi-CAD 18.53-Eintrag in
der Registry von Groß- in Kleinbuchstaben geändert
hat...).
Je nach Version von AS variieren die Hardware-Anforderungen deutlich:
Die DOS-Version läuft prinzipiell auf allen IBM-kompatiblen PCs,
angefangen vom PC/XT mit vierkommawenig Megaherz bis hin zum Pentium.
Wie bei vielen anderen Programmen aber auch, steigt der Lustgewinn
mit der Hardware-Ausstattung. So dürfte ein XT-Benutzer ohne
Festplatte erhebliche Probleme haben, die über 500 Kbyte
große Overlay-Datei von AS auf einer Diskette
unterzubringen...eine Festplatte sollte der PC also schon haben,
allein um vernünftige Ladezeiten zu erreichen. Im
Hauptspeicherbedarf ist AS recht genügsam: Das Programm selber
belegt knapp 300 Kbyte Hauptspeicher, AS sollte also ab einer
Hauptspeichergröße von 512 Kbyte ausführbar sein.
Die Version von AS für das DOS-Protected-Mode-Interface (DPMI)
benötigt zum Ablaufen mindestens einen 80286-Prozessor und 1
Mbyte freies extended memory. Daher stellen 2 Mbyte Hauptspeicher das
absolute Minimum dar, wenn man im XMS sonst keine anderen Spielereien
(Platten-Cache, RAM-Disk, hochgeladenes DOS) installiert hat, sonst
entsprechend mehr. Falls man die DPMI-Version in einer DOS-Box von
OS/2 laufen läßt, so sollte DPMI auch in den
DOS-Einstellungen der Box erlaubt sein (Einstellung An
oder Auto) und der Box eine entsprechende Menge von
XMS-Speicher zugeordnet sein. Die virtuelle Speicherverwaltung von
OS/2 sorgt hier übrigens dafür, daß man sich keine
Gedanken machen muß, ob der eingestellte Speicher auch real
verfügbar ist.
Die Hardware-Anforderungen der OS/2-Version ergeben sich
weitestgehend durch die des darunterliegenden Betriebssytemes, d.h.
mindestens ein 80386SX-Prozessor, 8 Mbyte RAM (bzw. 4 ohne grafische
Benutzeroberfläche) sowie ca 100..150 Mbyte Platz auf der
Festplatte. Da AS2 nur eine 16-Bit-Applikation ist, sollte er
theoretisch auch auf älteren OS/2-Versionen (und damit
80286-Prozessoren) lauffähig sein; ausprobieren konnte ich dies
aber nicht.
Die C-Version von AS wird im Quellcode ausgeliefert und erfordert
damit ein Unix- oder OS/2-System mit einem C-Compiler. Der Compiler
muß dem ANSI-Standard genügen (GNU-C erfüllt diese
Bedingung zum Beispiel). Ob Ihr UNIX-System bereits getestet und die
nötigen Definitionen vorgenommen wurden, können Sie der
README-Datei entnehmen. Als zur Kompilation benötigten
Plattenplatz sollten Sie ca. 15 Mbyte veranschlagen; dieser Wert (und
der nach der Übersetzung noch benötigte Platz für die
übersetzten Programme) variiert allerdings stark von System zu
System, so daß man diesen Wert nur als Richtschnur betrachten
sollte.
Je nach Plattform beinhalten die Distributionen eine unterschiedliche
Menge an Dateien. Dies ist zum einen darin begründet, daß
einige Pakete Dateien aus anderen Paketen mitbenutzen, zum anderen
benötigen bestimmte Pakete Zusatzdateien, die für den
Betrieb z.B. mit DOS-Extendern erforderlich sind. Falls eine der im
folgenden aufgeführten Dateien fehlt, hat jemand (im
Zweifelsfalle ich) beim Kopieren geschlafen...
Das DOS-Paket beinhaltet die in Tabelle 2.1 und 2.2 aufgeführten Dateien,die
sich grob in Programmdateien, Dokumentation, Includes und
Testprogramme aufteilen lassen:
Das Paket der DPMI-Version ist wesentlich kleiner, da sie weder
Include-Dateien noch Dienstprogramme noch Testprogramme enthält.
Diese können und müssen aus der DOS-Version übernommen
werden. Die Dienstprogramme wurden nicht gesondert für den
Protected Mode übersetzt, da sie einerseits vom
größeren Speicherplatz nicht profitieren würden,
andererseits aber die durch DPMI bedingte Verlangsamung erfahren
würden. Das Paket kann sich daher auf das AS-Executable selber
und den benötigten DOS-Extender beschränken (Tabelle 2.3).
Mit der OS/2-Version verhält es sich ähnlich wie mit der
DPMI-Version von AS, d.h. Include-Dateien und Testprogramme wurden
nicht extra hinzukopiert, aber die Dienstprogramme wurden
natürlich ,,nativ'' übersetzt (Tabelle 2.4). AS2MSG fehlt mangels Masse
(==kein Borland-Pascal für OS/2):
Da die C-Version im Gegensatz zu allen vorigen Versionen im Quellcode
ausgeliefert wird, ist ihr Lieferumfang deutlich umfangreicher
(zumindest so, daß dessen Auflistung an dieser Stelle in erster
Linie auf Papierverschwendung hinauslaufen würde...). So
beinhaltet er neben den Quellen auch eine im Vergleich zu den
Testprogrammen der DOS-Version ziemlich komplette Test-Suite, mit der
man die Funktion der frisch übersetzen Version verifizieren
kann.
Eine besondere Installation ist für die Nutzung von AS nicht
erforderlich, es genügt, alle EXE- sowie OVR-Dateien in ein
Verzeichnis kopieren, das in der PATH-Variable auftaucht. Ob
man ein bestehendes Verzeichnis nutzt oder ein neues anlegt, ist
dabei egal. Die Dokumentation, Beispielprogramme sowie Includefiles
können völlig nach belieben verteilt werden...als Beispiel
hier eine Installation, die ein UNIX-Anhänger vornehmen
würde:
Legen Sie folgende Verzeichnise an (im folgenden nehme ich an,
daß Sie AS auf Laufwerk C installieren wollen):
Da die DPMI-Version im wesentlichen ein Addon zur DOS-Version
für bestimmte Fälle darstellt, installiert man am besten
zuerst die DOS-Version nach obigem Schema und kopiert danach
zusätzlich AS2.EXE und die Dateien des DPMI-Servers in
das bin-Verzeichnis (DPMIUSER.DOC kann z.B. mit
ins DOC-Verzeichnis). Beim ersten Start kann es sein,
daß Sie auf 80286-Rechnern mit einer Meldung der folgenden Form
konfrontiert werden:
Die Installation der OS/2-Version kann in weiten Zügen genauso
ablaufen wie für die DOS-Version, nur daß man alle
EXE- und OVR-Dateien direkt entsorgen und durch die
standesgemäßen OS/2-Gegenstücke ersetzen kann. Im
Gegensatz zu DOS muß man bei OS/2 das Setzen der
ASCMD-Variablen in der CONFIG.SYS vornehmen (die
Position ist aber auch in diesem Fall beliebig).
Da die C-Version im Quellcode kommt, ist die Installation
naturgemäß etwas aufwendiger. Grob gesagt bestehen die
Schritte darin, die Makefiles anzupassen, die Übersetzung
anzuwerfen, einen Testlauf durchzuführen und die entstandenen
Executables sowie die Include-Dateien und Dokumentation zu
installieren. Die Details finden sich in der Datei README.
OS/2-Benutzer sollten unbedingt auch einen Blick in
README.OS2 werfen, um Schiffbruch zu vermeiden!
AS ist ein kommandozeilengesteuertes Programm, d.h. alle Parameter
und Dateiangaben sind in der Kommandozeile anzugeben.
Um den Speicherbedarf von AS unter DOS überhaupt befriedigen zu
können, wurden die verschiedenen Codegeneratormodule in der
DOS-Version in eine Overlaydatei verlegt, deren Existenz der
Assembler sofort nach Programmstart prüft. Findet er diese
nicht, so ist der Programmlauf auch schon an dieser Stelle zu
Ende...die Datei AS.OVR sollte sich immer im gleichen Verzeichnis wie
das EXE-File befinden. Die Verwendung von Overlays ergibt
naturgemäß etwas Overhead. AS versucht diesen aber zu
reduzieren, indem er eventuell vorhandenen EMS- oder XMS-Speicher zur
Ablage von Overlays nutzt. Sollte dies zu Problemen führen, so
können Sie die Verwendung von EMS bzw. XMS unterbinden, indem
Sie einer Environment-Variablen USEXMS bzw. USEEMS
den Wert n zuweisen. So kann man z.B. mit dem Befehl
Da AS alle Ein-und Ausgaben über das Betriebssystem abwickelt
(und daher unter DOS auch auf nicht ganz so kompatiblen PC's laufen
sollte) und eine rudimentäre Bildschirmsteuerung benötigt,
gibt er während der Assemblierung ANSI-Steuersequenzen aus.
Falls Sie in den Ausgaben von AS also seltsame Zeichen sehen sollten,
fehlt offensichtlich in Ihrer CONFIG.SYS die Einbindung des
ANSI-Treibers (device=ansi.sys), die weitere Funktion von AS
wird dadurch aber nicht beeinflußt. Alternativ können Sie
aber auch die Ausgabe von ANSI-Sequenzen durch das Setzen der
Environment-Variablen USEANSI auf n ganz
unterdrücken.
Der DOS-Extender der DPMI-Version läßt sich in seiner
Speicherbelegung durch diverse Kommandozeilenoptionen beeinflussen.
Diese können Sie bei Bedarf der Datei DPMIUSER.DOC entnehmen.
Zusätzlich ist ASX in der Lage, bei Bedarf den vorhandenen
Speicher durch eine Swap-Datei zu ,,erweitern''. Dazu belegt man eine
Environment-Variable ASXSWAP folgendermaßen:
Die verlängerte Ladezeit der DPMI-Version läßt sich
etwas durch Verwendung des Programmes RTMRES reduzieren. Es lädt
die Steuerprogramme resident und startet eine neue Shell, so
daß der Ladeoverhead bei mehrfachen Aufrufen entfällt. Mit
einem EXIT beenden Sie die Shell und entfernen die
Steuerprogramme wieder.
Im Gegensatz zu allen anderen Versionen ist die Sprache in die
C-Version nicht fest einkompiliert, sondern der passende Satz an
Meldungen wird zur Laufzeit aus einer Reihe von Nachrichtendateien
nachgeladen. AS sucht nach diesen Dateien in folgenden
Verzeichnissen:
Die Kommandozeilenparameter können grob in zwei Klassen
eingeteilt werden: Schalter und Dateispezifikationen. Parameter
dieser beiden Klassen können beliebig gemischt in der
Kommandozeile auftreten, AS wertet zuerst alle Parameter aus und
assembliert dann die angegebenen Dateien. Daraus folgen zwei Dinge:
Momentan sind folgende Schalter definiert:
Dieses Beispiel zeigt nebenbei, daß AS als Defaultendung
für Quelldateien ASM annimmt.
Etwas Vorsicht ist bei Schaltern angebracht, die ein optionales
Argument haben: Folgt auf einen solchen Schalter ohne Argument ein
Dateiname, so versucht AS, diesen als Argument zu verwerten, was
naturgemäß schief geht:
Neben der Angabe in der Kommandozeile können dauernd
benötigte Optionen in der Environment-Variablen ASCMD abgelegt
werden. Wer z.B. immer Listdateien haben möchte und ein festes
Includeverzeichnis hat, kann sich mit dem Befehl
Bei sehr langen Pfaden kann es jedoch auch in der ASCMD-Variablen eng
werden. Für solche Fälle kann auf eine sog. Key-
Datei ausgewichen werden, in der die Optionen genauso wie in der
Kommandozeile oder ASCMD-Variablen abgelegt werden können, nur
daß diese Datei mehrere Zeilen mit jeweils maximal 255 Zeichen
enthalten darf. Wichtig ist dabei, daß bei Optionen, die ein
Argument benötigen, sowohl Schalter als auch Argument in
einer Zeile stehen müssen. Der Name der Datei wird AS
dadurch mitgeteilt, daß er mit einem vorangestellten
Klammeraffen in der ASCMD-Variablen abgelegt wird, z.B.
Für den Fall, daß Sie AS von einem anderen Programm oder
einer Shell aufrufen wollen und diese Shell nur Klein- oder
Großbuchstaben in der Kommandozeile übergeben will,
existiert folgendes Workaround: Wird vor den Buchstaben der Option
eine Tilde gesetzt, so werden die folgenden Buchstaben immer als
Kleinbuchstaben interpretiert. Analog erzwingt ein Lattenzaun die
Interpretation als Großbuchstaben. Es ergeben sich z.B.
folgende Transformationen:
Abhängig vom Ablauf der Assemblierung endet der Assembler mit
folgenden Returncodes:
Zusätzlich endet jede Assemblierung einer Datei mit einer
kleinen Statistik, die Fehlerzahlen, Laufzeit, Anzahl der
Durchläufe und freien Speicher ausgibt. Bei eingeschaltetem
Assembler-Listing wird diese Statistik zusätzlich auch in das
Listing geschrieben.
OS/2 erweitert wie Unix das Datensegment einer Anwendung erst dann,
wenn sie wirklich mehr Speicher anfordert. Eine Angabe wie
Da es unter C auf verschiedenen Betriebssystemen keine kompatible
Möglichkeit gibt, den noch verfügbaren Speicher bzw. Stack
zu ermitteln, fehlen bei der C-Version diese beiden Angaben ganz.
Wie die meisten Assembler auch erwartet AS genau einen Befehl pro
Zeile (Leerzeilen sind natürlich auch zugelassen). Die Zeilen
dürfen nicht länger als 255 Zeichen werden, darüber
hinaus gehende Zeichen werden abgeschnitten.
Eine einzelne Zeile hat folgendes Format:
Einige Signalprozessorreihen von Texas Instruments verwenden den
für das Label vorgesehenen Platz wahlweise auch für einen
Doppelstrich (||), der die parallele Ausführung mit der
vorangehenden Instruktion anzeigt. Wenn diese beiden Instruktionen
auf Maschinenebene in einem einzigen Wort vereinigt werden (C3x),
macht ein zusätzliches Label vor der zweiten Anweisung
natürlich keinen Sinn und ist auch nicht vorgesehen. Anders
sieht es beim C6x mit seinen Instruktionspaketen variabler Länge
aus: Wer dort (unschönerweise...) mitten in ein Paket
hineinspringen will, muß das Label dafür in eine
Extrazeile davor setzen (das gleiche gilt übrigens auch für
Bedingungen, die aber zusammen mit dem Doppelstrich in einer Zeile
stehen dürfen).
Das Attribut wird von einer Reihe von Prozessoren benutzt, um
Spezialisierungen oder Kodierungsvarianten eines bestimmten Befehls
zu spezifizieren. Die bekannteste Nutzung des Attributs ist die
Angabe der Operandengröße, wie z. B. bei der 680x0-Familie
(Tabelle 2.5).
Da sich diese Anleitung nicht gleichzeitig als Handbuch für die
von AS unterstützten Prozessorfamilien versteht, ist dies leider
auch nicht der richtige Platz, um hier alle möglichen Attribute
für alle unterstützten Familien aufzuzählen. Es sei
aber angemerkt, daß i.a. nicht alle Befehle alle Attribute
zulassen, andererseits das Fortlassen eines Attributs meist zur
Verwendung der für diese Familie ,,natürlichen''
Operandengröße führt. Zum genaueren Studium greife
man auf ein Programmierhandbuch für die jeweilige Familie
zurück, z.B. in [1] für die
68000er.
Bei TLCS-9000, H8/500 und M16(C) dient das Attribut sowohl der Angabe
der Operandengröße, falls diese nicht durch die Operanden
klar sein sollte, als auch der des zu verwendenden Befehlsformates.
Dieses muß durch einen Doppelpunkt von der
Operandengröße getrennt werden, z.B. so:
Die Zahl der Befehlsparameter ist abhängig vom Befehl und kann
prinzipiell zwischen 0 und 20 liegen. Die Trennung der Parameter
voneinander erfolgt ausschließlich durch Kommas (Ausnahme:
DSP56xxx, dessen parallele Datentransfers durch Leerzeichen getrennt
werden), wobei in Klammern oder Hochkommas eingeschlossene Kommas
natürlich nicht beachtet werden.
Anstelle eines Kommentars am Ende kann die Zeile auch nur aus einem
Kommentar bestehen, wenn er in der ersten Spalte beginnt.
Bei den Leerzeichen zur Trennung einzelnen Komponenten darf es sich
genauso gut um Tabulatoren handeln.
Das von AS bei Angabe der Kommandozeilenoptionen l oder
L erzeugte Listing läßt sich grob in folgende Teile
gliedern:
Im ersten Teil listet AS den kompletten Inhalt aller Quelldateien
inklusive des erzeugten Codes auf. Eine Zeile in diesem Listing hat
dabei folgende Form:
Im Feld Zeile wird die Zeilennummer bezogen auf die
jeweilige Datei ausgegeben. Die erste Zeile einer Datei hat dabei
Nummer 1. Die Adresse, an der der für diese Zeile erzeugte Code
abgelegt wurde, folgt hinter dem Schrägstrich im Feld
Adresse.
Der erzeugte Code selber steht dahinter im Feld Code in
hexadezimaler Schreibweise. Je nach Prozessortyp und aktuellem
Segment können die Werte entweder als Bytes oder 16/32-Bit-Worte
formatiert sein. Sollte mehr Code erzeugt worden sein, als in das
Feld hineinpaßt, so werden im Anschluß an die Zeile
weitere Zeilen erzeugt, in denen nur dieses Feld belegt ist.
Im Feld Quelle schlußendlich wird die Zeile aus der
Quelldatei in ihrer Originalform ausgegeben.
Die Symboltabelle ist so ausgelegt, daß sie nach
Möglichkeit immer in 80 Spalten dargestellt werden kann.
Für Symbole ,,normaler Länge'' wird eine zweispaltige
Ausgabe gewählt. Sollten einzelne Symbole mit ihrem Wert die
Grenze von 40 Spalten überschreiten, werden sie in einer
einzelnen Zeile ausgegeben. Die Ausgabe erfolgt in alphabetischer
Reihenfolge. Symbole, die zwar definiert, aber nie benutzt wurden,
werden mit einem vorangestellten Stern (*) gekennzeichnet.
Die bisher genannten Teile sowie die Auflistung aller definierten
Makros / Funktionen lassen sich selektiv aus dem Gesamtlisting
ein-und ausblenden, und zwar mit dem bereits erwähnten
t-Kommandozeilenschalter. Intern existiert in AS ein Byte,
dessen Bits repräsentieren, welche Teile ausgegeben werden
sollen. Die Zuordnung von Bits zu den Teilen ist in Tabelle 2.6 aufgelistet.
Defaultmäßig sind alle Bits auf 1 gesetzt, bei Verwendung
des Schalters
In der Querverweisliste wird für jedes definierte Symbol in
alphabetischer Reihenfolge eine Ausgabe folgender Form erzeugt:
ACHTUNG! AS kann dieses Listing nur dann korrekt aufs Papier
bringen, wenn man ihm vorher die Länge und Breite des
Ausgabemediums mit Hilfe des PAGE-Befehls (siehe dort)
mitgeteilt hat! Der voreingestellte Default sind 60 Zeilen und eine
unbegrenzte Zeilenbreite.
Symbole dürfen zwar (wie in der Einleitung bereits angedeutet)
bis zu 255 Zeichen lang werden und werden auch auf der ganzen
Länge unterschieden, die Symbolnamen müssen aber einigen
Konventionen genügen:
Symbolnamen dürfen aus einer beliebigen Kombination von
Buchstaben, Ziffern, Unterstrichen und Punkten bestehen, wobei das
erste Zeichen keine Ziffer sein darf. Der Punkt wurde nur zugelassen,
um der MCS-51-Notation von Registerbits zu genügen, und sollte
möglichst nicht in eigenen Symbolnamen verwendet werden. Zur
Segmentierung von Symbolnamen sollte auf jeden Fall der Unterstrich
und nicht der Punkt verwendet werden.
Defaultmäßig ist AS nicht case-sensitiv, es ist also egal,
ob man Groß-oder Kleinbuchstaben verwendet. Mittels des
Kommandozeilenschalters U läßt sich AS jedoch in
einen Modus umschalten, in dem Groß- und Kleinschreibung
unterschieden wird. Ob AS umgeschaltet wurde, kann mit dem
vordefinierten Symbol CASESENSITIVE ermittelt werden: TRUE
bedeutet Unterscheidung, FALSE keine.
Tabelle 2.7 zeigt die wichtigsten,
von AS vordefinierten Symbole.
VORSICHT! Während es im case-insensitiven Modus egal
ist, mit welcher Kombination von Groß- und Kleinbuchstaben man
vordefinierte Symbole anspricht, muß man sich im
case-sensitiven Modus exakt an die oben angegebene Schreibweise (nur
Großbuchstaben) halten!
Zusätzlich definieren einige Pseudobefehle noch Symbole, die
eine Abfrage des damit momentan eingestellten Wertes
ermöglichen. Deren Beschreibung findet sich bei den
zugehörigen Befehlen.
Ein etwas verstecktes (und mit Vorsicht zu nutzendes) Feature ist,
Symbolnamen aus String-Variablen zusammenzubauen, indem man den Namen
des Strings mit geschweiften Klammern in den Symbolnamen einbaut. So
kann man z.B. den Namen eines Symbols anhand des Wertes eines anderen
Symbols festlegen:
Eine vollständige Auflistung aller von AS verwendeten
Symbolnamen findet sich in Anhang E.
Neben seinem Wert besitzt auch jedes Symbol eine Markierung, zu
welchen Segment es gehört. In erster Linie wird eine
solche Unterscheidung bei Prozessoren benötigt, die mehrere
Adreßräume besitzen. AS kann mit dieser Zusatzinformation
bei Zugriffen über ein Symbol warnen, wenn ein für diesen
Adreßraum ungeeigneter Befehl verwendet wird. Ein
Segmentattribut wird einem Symol automatisch angehängt, wenn es
als Label oder mit einem Spezialbefehl (z.B. BIT) definiert
wird; ein mit dem ,,Universalbefehl'' SET oder EQU
definiertes Symbol ist jedoch ,,typenlos'', d.h. seine Verwendung
wird niemals Warnungen auslösen. Das Segmentattribut eines
Symbols kann mit der eingebauten Funktion SYMTYPE abgefragt
werden, etwa so:
An den meisten Stellen, an denen der Assembler Zahlenangaben
erwartet, können nicht nur einfache Symbole oder Konstanten
angegeben werden, sondern ganze Formelausdrücke. Bei den
Komponenten der Formelausdrücke kann es sich sowohl um ein
einzelnes Symbol als auch um eine Konstante handeln. Konstanten
dürfen entweder Integer-, Gleitkomma-, oder Stringkonstanten
sein.
Integerkonstanten bezeichnen ganze Zahlen. Sie dürfen entweder
als eine Folge von Ziffern oder als eine Reihe von in
einfachen Hochkommas eingeschlossenen Zeichen geschrieben
werden. Werden sie als Ziffernfolgen geschrieben, so kann dies in
verschiedenen Zahlensystemen erfolgen, deren Kennzeichnung von
verwendeten Zielprozessor abhängt (Tabelle 2.9).
Falls das Zahlensystem nicht explizit durch vor-oder nachgestelle
Zeichen vorgegeben wird, nimmt AS die Basis an, die mit dem
RADIX-Befehl vorgegeben wurde (der Default dieser Einstellung
ist wiederum 10). Mit diesem Befehl lassen sich auch
,,ungewöhnliche" Zahlensysteme, d.h. andere als 2, 8, 10 oder 16
einstellen.
Gültige Ziffern sind die Zahlen 0 bis 9 sowie die Buchstaben A
bis Z (Wert 10 bis 35) bis zur Basis des Zahlensystems minus eins.
Die Verwendung von Buchstaben in Integerkonstanten bringt allerdings
auch einige Mehrdeutigkeiten mit sich, da Symbolnamen ja auch Ketten
aus Zahlen und Buchstaben sind: Ein Symbolname darf nicht mit einem
Zeichen von 0 bis 9 beginnen, was bedeutet, daß eine
Integerkonstante, die nicht durch ein anderes Sonderzeichen eindeutig
als solche erkennbar ist, niemals mit einem Buchstaben beginnen darf;
notfalls muß man eine eigentlich überflüssige Null
voranstellen. Der bekannteste Fall ist das Scheiben von
Hexadezimalkonstanten im Intel-Modus: Ist die vorderste Stelle
zwischen A und F, so hilft das hintangestellte H überhaupt
nichts, es muß noch eine Null davor (statt F0H also 0F0H). Die
Motorola-oder C-Syntax, die beide das Zahlensystem am Anfang einer
Integerkonstante kennzeichnen, kennen dieses Problem nicht.
(hihihi!).
Reichlich heimtückisch ist auch, daß bei immer
höheren, mit RADIX eingestellten Zahlensystemen, die
bei Intel- und C-Syntax benutzten Buchstaben zur Zahlensystemkennung
immer weiter ,,aufgefressen'' werden; so kann man z.B. nach RADIX
16 keine binären Konstanten mehr schreiben, und ab
RADIX 18 in Intel-Syntax auch keine hexadezimalen Konstanten
mehr. Also VORSICHT!
Mit Hilfe des RELAXED-Befehls (siehe Abschnitt 3.8.6) kann die starre Zuordnung einer
Schreibweise zu einem Zielprozessor aufgehoben werden, so daß
man eine beliebige Schreibweise verwenden kann (auf Kosten der
Kompatibilität zu Standard-Assemblern). Defaultmäßig
ist diese Option aber ausgeschaltet.
Wie bereits angesprochen, können Integer-Konstanten auch als
ASCII-Werte geschrieben werden, so entsprechen
Gleitkommazahlen werden in der üblichen halblogarithmischen
Schreibweise geschrieben, die in der allgemeinsten Form
Stringkonstanten müssen in doppelte Hochkommas (um sie
von den oben beschrieben ASCII-Integers zu unterscheiden)
eingeschlossen werden. Um nun aber auch Gänsefüßchen
und Sonderzeichen ohne Verrenkungen in String-Konstanten einbauen zu
können, wurde ein ,,Escape-Mechanismus'' eingebaut, der
Programmierer(inne)n aus C bekannt vorkommen dürfte:
Schreibt man einen Backslash mit einer maximal dreiziffrigen Zahl im
String, so versteht der Assembler dies als Zeichen mit dem
entsprechenden dezimalen ASCII-Wert. Alternativ kann der Zahlenwert
auch hexadezimal oder oktal mit einem vorangestellten x oder einer
vorangestellten 0 geschrieben werden. Für die hexadezimale
Schreibweise reduziert sich die Maximalanzahl von Stellen auf 2. So
kann man z.B. mit\3 ein ETX-Zeichen definieren. Vorsicht
allerdings mit der Definition von NUL-Zeichen! Da die C-Version von
AS momentan intern zur Speicherung von String-Symbolen C-Strings
benutzt (die durch NUL-Zeichen terminiert werden), sind NUL-Zeichen
in Strings momentan nicht portabel!
Einige besonders häufig gebrauchte Steuerzeichen kann man auch
mit folgenden Abkürzungen erreichen:
Über dieses Escape-Zeichen können sogar
Formelausdrücke in den String eingebaut werden, wenn sie in
geschweifte Klammern eingefaßt werden: z.B. ergibt
Bis auf den Einbau von Formelausdrücken ist dieser
Escape-Mechanismus auch in als ASCII definierten Integerkonstanten
zulässig, z.B. so:
Die Berechnung von im Formelausdruck entstehenden Zwischenergebnissen
erfolgt immer mit der höchsten verfügbaren Wortbreite, d.h.
32 Bit für Ganzzahlen, 80 Bit für Gleitkommazahlen und 255
Zeichen für Strings. Eine eventuelle Prüfung auf
Wertebereichsüberschreitung findet erst am Endergebnis statt.
Die portable C-Version kann nur mit 64-Bit-Gleitkommazahlen umgehen,
ist daher auf einen Maximalwert von ca. 10 308
beschränkt. Als Ausgleich werden auf einigen Plattformen
Integers mit 64 Bit Breite behandelt.
Der Assembler stellt zur Verknüpfung die in Tabelle 2.10 genannten Operanden zur Verfügung.
Unter ,,Rang'' ist dabei die Priorität zu verstehen, die dieser
Operator bei der Teilung eines Ausdruckes in Unterausdrücke hat,
der ranghöchste Operator wird also zuletzt ausgewertet.
Die Reihenfolge der Evaluierung läßt sich durch Klammerung
neu festlegen.
Die Vergleichsoperatoren liefern TRUE, falls die Bedingung zutrifft,
und FALSE falls nicht. Vergleiche betrachten Integerzahlen dabei als
32 Bit breit und vorzeichenbehaftet. Für die logischen
Operatoren ist ein Ausdruck TRUE, falls er ungleich 0 ist, ansonsten
FALSE.
Die Bitspiegelung ist wohl etwas erklärungsbedürftig: Der
Operator spiegelt die untersten Bits im ersten Operanden,
läßt die darüberliegenden Bits aber unverändert.
Die Zahl der zu spiegelnden Bits ist der rechte Operand und darf
zwischen 1 und 32 liegen.
Eine keine Fußangel beim binären Komplement: Da die
Berechnung grundsätzlich auf 32- oder 64-Bit-Ebene erfolgt,
ergibt seine Anwendung auf z.B. 8-Bit-Masken üblicherweise
Werte, die durch voranstehende Einsen nicht mehr im entferntesten in
8-Bit-Zahlen hineinpassen. Eine binäre UND-Verknüpfung mit
einer passenden Maske ist daher unvermeidlich!
Zusätzlich zu den Operatoren definiert der Assembler noch eine
Reihe in erster Linie transzendenter Funktionen mit
Gleitkommaargument, die Tabellen 2.11
und 2.12 auflisten.
Die Funktionen FIRSTBIT, LASTBIT und
BITPOS liefern als Ergebnis -1, falls überhaupt kein bzw.
nicht genau ein Bit gesetzt ist. Zusätzlich gibt BITPOS
in einem solchen Fall eine Fehlermeldung aus.
Die String-Funktion SUBSTR erwartet als ersten Parameter den
Quellstring, als zweiten die Startposition und als dritten die Anzahl
zu extrahierender Zeichen (eine 0 bedeutet, alle Zeichen bis zum Ende
zu extrahieren). STRSTR liefert das erste Auftreten des
zweiten Strings im ersten bzw. -1, falls das Suchmuster nicht
gefunden wurde. Beide Funktionen numerieren die Zeichen in einem
String ab 0 durch!
Wenn eine Funktion auch Gleitkommaargumente erwartet, so soll dies
nicht bedeuten, daß man nicht z.B.
Schaltet man AS in den case-sensitiven Modus, so können im
Gegensatz zu vordefinierten Symbolen die vordefinierten Funktionen
weiterhin in beliebiger Schreibweise angesprochen werden. Bei
selbstdefinierten Funktionen (siehe Abschnitt 3.4.7 wird allerdings unterschieden.
Dies hat zur Folge, daß z.B. bei der Definition einer
Funktion Sin man mit Sin diese Funktion auch
erreicht, mit allen anderen Schreibweisen jedoch die eingebaute
Funktion.
Für die korrekte Umwandlung von Klein-zu Großbuchstaben
ist eine DOS-Version >= 3.30 erforderlich.
Dieser Abschnitt ist das Produkt eines gewissen Grolls auf die
(durchaus legale) Art und Weise, wie einige Leute programmieren, die
in Zusammenhang mit AS bisweilen das eine oder andere Problem
verursachen kann. Die Rede ist hier von sogenannten
,,Vorwärtsreferenzen''. Was unterscheidet eine
Vorwärtsreferenz von einer normalen Referenz? Dazu sehe man sich
folgendes Programmbeispiel an (man sehe mir bitte meine -- auch im
Rest dieser Anleitung anzutreffende -- 68000-Lastigkeit nach):
Leider ist die Sache im Falle von Assembler nicht so einfach, denn
man will ja bisweilen auch vorwärts im Code springen oder
muß aus bestimmten Gründen Variablendefinitionen hinter
den Code verlegen. Dies ist im Beispiel der Fall für den
bedingten Sprung, mit dem ein anderer Befehl übersprungen wird.
Wenn der Assembler im ersten Durchlauf auf den Sprungbefehl trifft,
so sieht er sich mit der Situation konfrontiert, entweder die
Teilfelder der Instruktion, die die Sprungadresse beinhalten,
leerzulassen, oder seitens des Formelparsers (der das
Adreßargument ja auswerten muß) anstelle des korrekten,
aber unbekannten Wertes einen Wert anzubieten, der ,,niemandem
wehtut''. Bei einem einfachen Assembler, der nur eine Zielarchitektur
kennt und bei dem sich die betroffenen Befehle an einer Hand
abzählen lassen, wird man sicher die erste Variante wählen,
bei AS mit seinen vielen Dutzend Zielen wäre die Zahl der
Sonderabfragen aber extrem hoch geworden, so daß nur der zweite
Weg in Frage kam: Falls im ersten Pass ein unbekanntes Symbol
auftaucht, so liefert der Formelparser den momentanen Stand des
Programmzählers als Ergebnis zurück! Nur dieser Wert ist
geeignet, relativen Sprüngen mit Sprungdistanzen unbekannter
Länge eine Adresse anzubieten, die nicht zu Fehlern führt.
Dies beantwortet auch die bisweilen gestellte Frage, warum in einem
Listing des ersten Passes (dies bleibt z.B. stehen, wenn AS aufgrund
anderer Fehler den zweiten Pass erst gar nicht beginnt), z.T. falsche
Adressen im erzeugten Binärcode gezeigt werden - dies sind noch
nicht aufgelöste Vorwärtsreferenzen.
Das obige Beispiel offenbart allerdings noch eine weitere
Schwierigkeit von Vorwärtsreferenzen: Je nach Abstand von Quelle
und Ziel im Code kann der Sprungbefehl entweder lang oder kurz sein.
Diese Entscheidung über die Code-Länge - und damit auch die
Adressen folgender Labels - kann jedoch mangels genauer Kenntnis der
Zieladresse im ersten Pass nicht erfolgen. Sofern der Programmierer
nicht explizit kenntlich gemacht hat, ob der Sprung lang oder kurz
sein soll, behelfen sich reine 2-Pass-Assembler wie ältere
MASM-Versionen von Microsoft damit, im ersten Pass (nach diesem
müssen alle Adressen festliegen) Platz für die längste
Version zu reservieren und im zweiten Pass den
überschüssigen Platz mit NOPs aufzufüllen.
AS-Versionen bis 1.37 taten dieses ebenfalls, danach bin ich auf das
Multipass-Verfahren übergegangen, das die strenge Einteilung in
zwei Passes aufhebt und beliebig viele Durchgänge erlaubt. Dazu
wird im ersten Pass der optimale Code mit den angenommenen
Symbolwerten erzeugt. Stellt AS fest, daß im zweiten Pass durch
Codelängenveränderungen sich Werte von Symbolen
geändert haben, so wird einfach noch ein dritter Pass eingelegt,
und da durch die neuen Symbolwerte des zweiten Passes auch im dritten
Pass sich der Code wieder verkürzen oder verlängern kann,
ist ein weiterer Pass nicht unmöglich. Ich habe schon
8086-Programme erlebt, bei denen erst nach 12 Durchgängen alles
stimmte. Leider erlaubt dieser Mechanismus nicht die Vorgabe einer
Maximalzahl von Durchläufen, ich kann als Regel nur sagen,
daß die Anzahl von Durchläufen sinkt, je mehr man davon
Gebrauch macht, Sprung- oder Adreßlängen explizit
vorzugeben.
Speziell bei großen Programmen kann es zu einer interessanten
Situation kommen: Die Lage eines vorwärts gerichteten Sprunges
hat sich im zweiten Pass so weit gegenüber dem ersten
verschoben, daß der jetzt noch benutzte Label-Wert aus dem
ersten Pass außerhalb der erlaubten Sprungdistanz liegt. AS
berücksichtigt solche Situationen, indem er jegliche
Fehlermeldungen über zu weite Sprungdistanzen unterdrückt,
sobald er erkannt hat, daß er wegen sich ändernder
Symbolwerte ohnehin einen weiteren Durchlauf machen muß. Dies
funktioniert zwar in 99% aller Fälle, es gibt jedoch auch
Konstrukte, in denen der erste, derartig kritische Befehl bereits
auftaucht, bevor AS eine Chance hat, zu erkennen, daß ein neuer
Pass erforderlich ist. Das folgende Beispiel konstruiert eine solche
Situation mit Hilfe einer Vorwärtsreferenz (und war der
Anlaß für die Überschrift dieses Abschnitts...):
Zugegeben, das war ein ziemlich länglicher Ausflug, aber es
mußte einfach einmal sein. Was sollte man als Erkenntnis aus
diesem Abschnitt mitnehmen?
Manchmal ist es erwünscht, nicht nur einer Speicheradresse oder
einer Konstanten, sondern auch einem Register einen symbolischen
Namen zuzuweisen, um seine Funktion in einem bestimmten
Programmabschnitt zu verdeutlichen. Dies ist bei Prozessoren, die die
Register schlicht als einen weiteren Adreßraum behandeln, recht
problemlos, da als Register damit auch Zahlenausdrücke erlaubt
sind und man solche Symbole mit schlichten EQUs definieren
kann (z.B. bei MCS-96 oder TMS7000). Bei den allermeisten Prozessoren
jedoch sind Registernamen festgelegte Literale, und AS behandelt sie
aus Geschwindigkeitsgründen gesondert, so daß ein
besonderer Mechanismus vonnöten ist, um symbolische Register zu
definieren. Ein Registersymbol wird üblicherweise durch die
REG-Anweisung definiert und hat ansonsten die gleiche Form wie
eine EQU-Definition. Sie unterliegt jedoch einer Reihe von
Einschränkungen: Zum einen ist ein Registersymbol eine reine 'as
is' gespeicherte Zeichenkette, die auch nur in dieser Form verwendet
werden kann. Es ist also z.B. keine Arithmetik möglich, um aus
einem Register den Nachfolger zu berechnen, etwa so:
Registersymbole sind analog zu normalen Symbolen lokal zu Sektionen,
und es ist auch durch Anhängen eines in eckige Klammern
gesetzten Sektionsnamens möglich, auf ein Registersymbol aus
einer bestimmten Sektion zuzugreifen. Aufgrund der fehlenden
Möglichkeit zur Vorwärtsreferenz gibt es aber keine
Entsprechung zur FORWARD-Direktive, und da Registersymbole
im allgemeinen nur in einem sehr eng umschränkten Kontext eine
Bedeutung haben, ist ein Export per PUBLIC oder
GLOBAL auch nicht vorgesehen.
Sind in einem Kontext ein normales als auch ein Registersymbol
gleichen Namens bekannt, so wird immer das Registersymbol vorgezogen.
Dies ist aber nicht der Fall, wenn der Name nicht alleine, sondern
eingebunden in einen Ausdruck steht (dazu reichen Klammern!), dann
wird das normale Symbol benutzt.
Diese Funktion ist ein Abfallprodukt aus den reinen
68000er-Vorgängern von AS, da sie vielleicht doch der (die?!)
eine oder andere gebrauchen könnte, habe ich sie dringelassen.
Grundproblem ist es, an bestimmte beim Assemblieren entstehende
Symbole heranzukommen, weil man evtl. mit diesen
Adreßinformationen auf den Speicher des Zielsystems zugreifen
möchte. Der Assembler erlaubt es, mit Hilfe des
SHARED-Pseudobefehles (siehe dort) Symbolwerte extern zur
Verfügung zu stellen. Zu diesem Zweck erstellt der Assembler im
zweiten Pass eine Textdatei mit den gewünschten Symbolen und
ihren Werten, die mittels Include in ein Hochsprachen-oder weiteres
Assemblerprogramm eingebunden werden können. Das Format der
Textdatei (C, Pascal oder Assembler) wird durch die
Kommandozeilenschalter p, c oder a
festgelegt.
ACHTUNG! Ist keiner dieser Schalter angegeben, so wird auch
keine Datei erzeugt, egal ob sich SHARED-Befehle im
Quelltext finden oder nicht!
AS prüft beim Anlegen der Share-Datei nicht, ob bereits eine
Datei gleichen Namens existiert, eine solche wird ggfs. einfach
überschrieben. Eine Abfrage halte ich nicht für sinnvoll,
da AS dann bei jedem Lauf fragen würde, ob er die alte Version
der Share-Datei überschreiben darf, und das wäre doch sehr
lästig...
Mit Varianten gängiger Mikrocontroller-Familien ist es wie mit
Kaninchen: Sie vermehren sich schneller, als man mit der Versorgung
hinterherkommen kann. Im Zuge der Entwicklung von Prozessorkernen als
Bausteine für ASICs und von Controller-Familien mit vom Kunden
wählbarer Peripherie wird die Zahl von Controller-Varianten, die
sich von einem bekannten Typ nur in einigen Peripherie-Details
unterscheiden, immer größer. Die Unterscheidung der
einzelnen Typen ist aber trotz meist identischer Prozessorkernes
wichtig, um z.B. in den Includefiles den korrekten Satz von
Peripherieregistern einzublenden. Bisher habe ich mich zwar immer
bemüht, die wichtigsten Vertreter einer Familie in AS einzubauen
(und werde das auch weiter tun), aber manchmal läuft mir die
Entwicklung einfach auf und davon...es mußte also ein
Mechanismus her, mit dem man die Liste der unterscheidbaren
Prozessortypen selbst erweitern kann.
Das Ergebnis davon sind Prozessor-Aliasse: Mit der
Kommandozeilenoption alias kann man einen neuen Prozessortyp
definieren, der im Befehlssatz einem anderen, in AS fest eingebauten
Typ entspricht. Bei Benutzung dieses Typs im CPU-Befehl wird
sich AS also wie beim ,,Original'' verhalten, mit einem Unterschied:
Die Variablen MOMCPU bzw. MOMCPUNAME werden auf den
Namen des Alias gesetzt, wodurch der neue Name zur Unterscheidung
z.B. in Includefiles dienen kann.
Die Definition dieser Aliasse wurde aus zwei Gründen mit
Kommandozeilenoptionen anstatt Pseudobefehlen vorgenommen: zum einen
wäre es ohnehin nicht möglich gewesen, die Definition der
Aliasse zusammen mit den Registerdefinitionen in eine Include-Datei
zu legen, denn in einem Programm, das so eine Datei benutzen wollte,
müßte sie ja sowohl vor als auch nach dem
CPU-Befehl in der Hauptdatei eingebunden werden - eine
Vorstellung, die irgendwo zwischen unelegant und unmöglich
liegt. Zum zweiten ermöglicht diese Implementierung, die
Definition der neuen Typen in eine Datei zu legen, die über
die ASCMD-Variable beim Start automatisch ausgeführt
wird, ohne das sich das Programm darum kümmern müßte.
Nicht für alle Prozessoren sind alle Pseudobefehle definiert.
Vor der Beschreibung eines Befehls ist deshalb jeweils vermerkt,
für welche Prozessortypen dieser Befehl erlaubt ist.
Gültigkeit: alle Prozessoren
SET und EQU erlauben die Definition typenloser
Konstanten, d.h. sie werden keinem Segment zugeordnet und ihre
Verwendung erzeugt in keinem Fall eine Warnung wegen
Segmentverquickung. Während EQU Konstanten definiert,
die nicht wieder (mit EQU) geändert werden können,
erlaubt SET die Definition von Variablen, die sich
während des Assemblerlaufes verändern lassen. Dies ist
nützlich z.B. bei der Allokation von Resourcen à la
Interruptvektoren, wie im folgenden Beispiel:
Mit EQU/SET lassen sich Konstanten aller Typen definieren,
z.B.
Anstelle von EQU darf auch einfach ein Gleichheitszeichen
geschrieben werden, analog kann man anstelle von SET
bzw. EVAL einfach := schreiben.
Defaultmäßig sind mit SET oder EQU
definierte Symbole typenlos, optional kann jedoch als zweites
Argument ein Segmentname (CODE, DATA, IDATA, XDATA, YDATA,
BITDATA, IO oder REG) oder MOMSEGMENT für das
aktuell gesetzte Segment angegeben werden, um das Symbol einem
bestimmten Adreßraum zuordnen. AS berücksichtigt dabei
nicht, ob der benutzte Adreßraum bei dem aktuell gesetzten
Zielprozessor auch vorhanden ist!
Gültigkeit: diverse, SFRB nur MCS-51
Diese Befehle funktionieren wie EQU, nur sind die damit
definierten Symbole dem direkt adressierbaren Datensegment
zugeordnet, d.h. sie dienen bevorzugt zur Definition von RAM-Zellen
und (wie der Name ahnen läßt) im Datenbereich
eingeblendeten Hardwareregistern. Der dabei zugelassene Wertebereich
ist identisch mit dem bei ORG für das
DATA-Segment zugelassenen (s. Abschnitt 3.2.1). SFR und SFRB
unterscheiden sich darin, daß SFRB das Register als
bitadressierbar kennzeichnet, weshalb AS zusätzlich 8 Symbole
erzeugt, die dem Bitsegment zugeordnet werden und die Namen
xx.0 bis xx.7 tragen, z.B.
AS überprüft bei der Definition eines bitadressierbaren
Registers mit SFRB, ob die Speicherstelle überhaupt
bitadressierbar ist (Bereich 20h..3fh bzw. 80h, 88h, 90h,
98h...0f8h). Ist sie es nicht, so wird eine Warnung ausgegeben; die
dann erzeugten Bit-Symbole sind undefiniert.
Gültigkeit: DSP56xxx
Auch der DSP56000 hat einige Peripherieregister memory-mapped im
Speicher liegen, die Sache wird jedoch dadurch komplizierter,
daß es zwei Datenbereiche gibt, den X-und Y-Bereich. Diese
Architektur erlaubt einerseits zwar einen höheren
Parallelitätsgrad, zwingt jedoch andererseits dazu, den
normalen SFR-Befehl in die beiden oben genannten Varianten
aufzuspalten. Sie verhalten sich identisch zu SFR, nur
daß XSFR ein Symbol im X-Adreßraum definiert
und YSFR entsprechend eines im Y-Adreßraum. Der
erlaubte Wertebereich ist 0..$ffff.
Gültigkeit: alle Prozessoren
Die Funktion des LABEL-Befehls ist identisch zu
EQU, nur wird das Symbol nicht typenlos, sondern erhält das
Attribut ,,Code''. LABEL wird genau für einen Zweck
benötigt: Labels in Makros sind normalerweise lokal, also nicht
außerhalb des Makros zugreifbar. Mit einem EQU-Befehl
kann man sich zwar aus der Affäre ziehen, die Formulierung
Gültigkeit: MCS-(2)51, XA, 80C166, 75K0, ST9
BIT dient dazu, ein einzelnes Bit einer Speicherstelle mit
einem symbolischen Namen gleichzusetzen. Da die Art und Weise, wie
verschiedene Prozessoren Bitverarbeitung und -adressierung betreiben,
stark variiert, verhält sich auch dieser Befehl je nach
Zielplattform anders:
Für die MCS/51-Familie, die einen eigenen Adreßraum
für Bitoperanden besitzt, ist die Funktion von BIT ganz
analog zu SFR, d.h. es wird einfach ein Integer-Symbol mit
dem angegebenen Wert und dem Segment BDATA erzeugt. Für alle
anderen Prozessoren wird die Bitadressierung dagegen zweidimensional
mit Adresse und Bitstelle vorgenommen. In diesem Fall verpackt AS
beide Teile in einer vom jeweiligen Prozessor abhängigen Weise
in ein Integer-Symbol und dröselt dieses bei der Benutzung
wieder in die beiden Teile auseinander. Letzterer Fall trifft auch
schon für den 80C251 zu: Während zum Beispiel der Befehl
Noch etwas weiter geht der BIT-Befehl bei der 75K0-Familie:
Da dort Bitadressierungen nicht nur absolute Basisadressen verwenden
dürfen, sind sogar Ausdrücke wie
Beim ST9 ist es hingegen möglich, Bits auch invertiert
anzusprechen, was beim BIT-Befehl auch berücksichtigt
wird:
Gültigkeit: TMS 370xxx
Die TMS370-Reihe hat zwar kein explizites Bit-Segment, jedoch
können einzelne Bits als Symbol durch diesen Befehl simuliert
werden. DBIT benötigt zwei Operanden, nämlich
einmal die Adresse der Speicherstelle, in der das Bit liegt, sowie
die genaue Position des Bits im Byte. So definiert man z.B. mit
Gültigkeit: 8080/8085/8086, XA, Z80, 320xx, TLCS-47,
AVR
PORT arbeitet analog zu SFR, nur wird das Symbol
dem I/O-Adreßbereich zugeordnet. Erlaubte Werte sind 0..7 beim
3201x, 0..15 beim 320C2x, 0..65535 beim 8086, 0..63 beim AVR und
0..255 beim Rest.
Beispiel: eine PIO 8255 liege auf Adresse 20H:
Gültigkeit: AVR, M*Core, ST9, 80C16x
Obwohl immer mit gleicher Syntax, hat diese Anweisung von Prozessor
zu Prozessor eine leicht abweichende Bedeutung: Falls der
Zielprozessor für Register einen eigenen Adreßraum
verwendet, so hat REG die Wirkung eines simplen
EQUs für eben diesen Adreßraum (z.B. beim ST9).
Für alle anderen Prozessoren definiert REG
Registersymbole, deren Funktion in Abschnitt 2.10 beschrieben sind.
Gültigkeit: 8X30x
LIV und RIV dienen dazu, sogenannte IV-Bus-Objekte
zu definieren. Bei diesen handelt es sich um Bitgruppen in peripheren
Speicherzellen mit einer Länge von 1..8 Bit, die fortan
symbolisch angesprochen werden können, so daß man bei den
entsprechenden Befehlen nicht mehr Adresse, Länge und Position
separat angeben muß. Da die 8X30x-Prozessoren zwei periphere
Adreßräume besitzen (einen ,,linken'' und einen
,,rechten'', sind auch zwei separate Befehle definiert. Die Parameter
dieser Befehle sind allerdings identisch: es müssen drei
Parameter sein, die Adresse, Startposition und Länge angeben.
Weitere Hinweise zur Benutzung von Busobjekten finden sich in
Abschnitt 4.16.
Gültigkeit: alle Prozessoren
Einplatinensysteme, zumal wenn sie LCDs ansteuern, benutzen
häufig einen anderen Zeichensatz als ASCII, und daß die
Umlautkodierung mit der im PC übereinstimmt, dürfte wohl
reiner Zufall sein. Um nun aber keine fehlerträchtigen
Handumkodierungen vornehmen zu müssen, enthält der
Assembler eine Umsetzungstabelle für Zeichen, die jedem
Quellcode ein Zielzeichen zuordnet. Zur Modifikation dieser Tabelle
(die initial 1:1 übersetzt), dient der Befehl
CHARSET. CHARSET kann mit verschiedenen Parameterzahlen
und -typen angewendet werden. Ist die Parameterzahl eins, so
muß es sich um einen String-Ausdruck handeln, der von AS als
Dateiname interpretiert wird. Aus dieser Datei liest AS dann die
ersten 256 Bytes aus und kopiert sie in die Übersetzungstabelle.
Hiermit lassen sich also komplexere, extern erzeugte Tabellen in
einem Schlag aktivieren. In allen anderen Varianten muß der
erste Parameter ein Integer im Bereich von 0 bis 255 sein, der den
Startpunkt der in der Übersetzungstabelle zu modifizierenden
Einträge angibt. Es folgen dann ein oder zwei weitere Parameter,
die die Art der Übersetzung angeben:
Ein einzelner, weiterer Integer verändert genau einen Eintrag.
So bedeutet z.B.
In der letzten Variante folgt nach dem Startindex ein String, der die
ab dem Startindex abzulegenden Zeichen angibt. Das letzte Beispiel
könnte man also auch so formulieren:
CHARSET kann auch ganz ohne Parameter aufgerufen werden,
allerdings mit ziemlich gründlichen Folgen: Dies bewirkt eine
Reinitialisierung der Übersetzungstabelle in ihren Urzustand,
d.h. man bekommt wieder eine 1:1-Übersetzung.
ACHTUNG! CHARSET beeinflußt nicht nur im
Speicher abgelegte Stringkonstanten, sondern auch als ,,ASCII''
formulierte Integerkonstanten. Dies bedeutet, daß eine evtl.
bereits modifizierte Umsetzungstabelle in den obigen Beispielen zu
anderen Ergebnissen führen kann!
Gültigkeit: alle Prozessoren
Mit der CHARSET-Anweisung hat man zwar beliebige Freiheiten
in der Zeichenzuordnung zwischen Entwicklungs- und Zielplattform,
wenn auf der Zielplattform jedoch verschiedene Zeichensätze
existieren, kann das Umschalten zwischen diesen jedoch zu einer
umständlichen Orgie von CHARSET-Kommandos werden. Mit
der CODEPAGE-Anweisung kann man jedoch mehrere
Zeichentabellen vorhalten und zwischen diesen mit einem Befehl
umschalten. Als Parameter erwartet CODEPAGE ein oder zwei
Namen: zum einen den Namen der fortan zu benutzenden Tabelle, zum
anderen optional den Namen der Tabelle, die die initiale Belegung der
Tabelle vorgibt (dieser Parameter hat somit auch nur eine Bedeutung
beim ersten Umschalten auf eine Tabelle, bei der AS sie automatisch
anlegt). Fehlt der zweite Parameter, so ist die initiale Belegung der
neuen Tabelle gleich der vorher aktiven Tabelle. Alle folgenden
CHARSET-Anweisungen verändern nur die momentan aktive
Tabelle.
Zu Beginn eines Durchlaufes wird von AS automatisch eine einzelne
Zeichentabelle mit dem Namen STANDARD erzeugt und 1:1
vorbelegt. Verwendet man keine CODEPAGE-Anweisungen, so
beziehen sich alle mit CHARSET gemachten Einstellungen auf
diese Tabelle.
Gültigkeit: alle Prozessoren
ENUM dient analog zu dem entsprechenden Befehl in C dazu,
Aufzählungstypen zu definieren, d.h. eine Reihe von
Integer-Konstanten, denen fortlaufende Werte (von 0 an beginnend)
zugewiesen werden. Als Parameter werden dabei die Namen der zu
definierenden Symbole angegeben, wie in dem folgenden Beispiel:
ENUM-Befehle sind von Hause aus einzeilig, d.h. bei einem
neuen ENUM-Befehl beginnt die Numerierung wieder bei Null.
Mehrzeilige Aufzählungen kann man aber mit einem kleinen Trick
erreichen, der die Tatsache ausnutzt, daß man mit einer
expliziten Zuweisung den internen Zähler neu setzen kann, wie in
dem folgenden Fall:
Gültigkeit: alle Prozessoren
Auch in Assemblerprogrammen ergibt sich dann und wann die
Notwendigkeit, analog zu Hochsprachen zusammengesetzte
Datenstrukturen zu definieren. AS unterstützt dies mit den
Befehlen STRUCT und ENDSTRUCT, die die Definition
einer solchen Struktur einleiten bzw. abschließen. Das
Verfahren ist simpel: Mit einem STRUCT wird der momentane
Programmzähler gesichert und auf Null zurückgesetzt. Alle
Labels ergeben mithin die Offsets der einzelnen Datenfelder in der
Struktur. Die Reservierung des Platzes für die einzelnen Felder
erfolgt mit den für den jeweils aktiven Zielprozessor
zulässigen Befehlen zur Speicherplatzreservierung, also z.B.
DS.x für die Motorolas oder DB & Co. für
Intels. Das dem STRUCT-Befehl vorangestellte (nicht
optionale) Label ist der Name des Records und kann optional beim
ENDSTRUCT-Befehl wiederholt werden. Weiterhin legt
ENDSTRUCT in <Name>_len die Gesamtlänge der
Struktur ab (man kann auch die Verwendung eines anderen Symbolnamens
erzwingen, indem man dessen Name als Argument von ENDSTRUCT
angibt). In der Definition
STRUCT-Definitionen dürfen auch geschachtelt werden;
nach Beendigung der inneren STRUCT-Definition wird dann der
Adreßzähler der äußeren Struktur automatisch um
die Größe der inneren Struktur inkrementiert (die
Zählung innerhalb der inneren Struktur läuft natürlich
ab 0).
Um Mehrdeutigkeiten bei gleichnamigen Feldern in verschiedenen
Strukturen zu vermeiden, stellt AS den Feldnamen
defaultmäßig den Namen der Struktur, mit einem Unterstrich
getrennt, voran. Im obigen Beispiel würden also die Symbole
Rec_Ident, Rec_Pad und Rec_Pointer erzeugt. Diese
Verhalten läßt sich unterbinden, indem man als Parameter
der STRUCT-Anweisung ein NOEXTNAMES anbringt. Bei
geschachtelten Strukturdefinitionen funktioniert dies
sinngemäß, d.h Feldnamen werden um die Namen aller sie
umschließenden Strukturen erweitert, die keine
NOEXTNAMES-Direktive bekamen.
Gültigkeit: alle Prozessoren
Mit PUSHV und POPV ist es möglich, den Wert
von (nicht makrolokalen) Symbolen temporär zu speichern und zu
einem späteren Zeitpunkt wiederherzustellen. Die Speicherung
erfolgt auf Stacks, d.h. Last-In-First-Out-Speichern. Ein
Stack hat einen Namen, der den allgemeinen Symbolkonventionen
genügen muß, und existiert so lange, wie er mindestens ein
Element enthält: Ein bisher nicht existierender Stack wird
bei PUSHV automatisch angelegt, ein durch POPV leer
werdender Stack wird automatisch wieder aufgelöst. Der Name des
Stacks, auf den Symbole abgelegt und von dem sie wieder abgeholt
werden sollen, ist der erste Parameter von PUSHV bzw.
POPV, danach folgt eine beliebige Menge von Symbolen als weitere
Parameter. Alle in der Liste aufgeführten Symbole müssen
bereits existieren, es ist also nicht möglich, mit
einem POPV-Befehl implizit neue Symbole zu definieren.
Stacks stellen eine globale Ressource dar, d.h. ihre Namen sind nicht
lokal zu Sektionen.
Wichtig ist, daß die Variablenliste immer von links
nach rechts abgearbeitet wird. Wer also mehrere Variablen mit
POPV von einem Stack herunterholen will, muß diese in
genau umgekehrter Reihenfolge zum entsprechenden PUSHV
angeben!
Der Name des Stacks kann auch weggelassen werden, etwa so:
Nach Ende eines Durchlaufes überprüft AS, ob noch Stacks
existieren, die nicht leer sind, und gibt deren Namen sowie
,,Füllstand'' aus. Mit diesen Warnungen kann man herausfinden,
ob an irgendeiner Stelle die PUSHV's und POPV's
nicht paarig sind. Es ist jedoch in keinem Fall möglich,
Symbolwerte in einem Stack über mehrere Durchläufe
hinwegzuretten: Zu Beginn eines Durchlaufes werden alle Stacks
geleert!
Gültigkeit: alle Prozessoren
ORG erlaubt es, den assemblerinternen
Adreßzähler mit einem neuen Wert zu besetzen. Der
Wertebereich ist vom momentan gewählten Segment und vom
Prozessortyp abhängig (Tabellen 3.1
bis 3.4). Die untere Grenze ist dabei
immer 0; die obere Grenze der angegebene Wert minus eins.
Falls in einer Familie verschiedene Varianten unterschiedlich
große Adreßräume haben, ist jeweils der maximale
Raum aufgeführt.
ORG wird in erster Linie benötigt, um dem Code eine neue
Startadresse zu geben und damit verschiedene, nicht
zusammenhängende Codestücke in einer Quelldatei
unterzubringen. Sofern nicht in einem Feld explizit anders angegeben,
ist die vorgegebene Startadresse in einem Segment (d.h. die ohne
ORG angenommene) immer 0.
Gültigkeit: alle Prozessoren
Mit diesem Befehl wird festgelegt, für welchen Prozessor im
weiteren Code erzeugt werden soll. Die Befehle der anderen
Prozessorfamilien sind dann nicht greifbar und erzeugen eine
Fehlermeldung!
Die Prozessoren können grob in Familien unterschieden werden, in
den Familien dienen unterschiedliche Typen noch einmal zur
Feinunterscheidung:
Beim CPU-Befehl muß der Prozessortyp als einfache Konstante
angegeben werden, eine Berechnung à la
Dieses Feature kann man vorteilhaft einsetzen, um je nach
Prozessortyp unterschiedlichen Code zu erzeugen. Der 68000 z.B. kennt
noch keinen Befehl für den Unterprogrammrücksprung mit
Stapelkorrektur. Mit der Variablen MOMCPU kann man ein Makro
definieren, das je nach Prozessortyp den richtigen Befehl benutzt
oder ihn emuliert:
Implizit schaltet der Assembler mit dem CPU-Befehl das
aktuelle Segment wieder auf Code zurück, da dies das einzige
Segment ist, das alle Prozessoren definieren.
Default für den Prozessortyp ist 68008.
Mit diesen drei Schaltern kann bestimmt werden, auf welche Teile des
Befehlssatzes verzichtet werden soll, weil die dafür
nötigen Vorbedingungen im folgenden Codestück nicht gegeben
sind. Als Parameter für diese Befehle darf entweder ON
oder OFF gegeben werden, der momentan gesetzte Zustand kann
aus einer Variablen ausgelesen werden, die entweder TRUE oder FALSE
ist.
Die Befehle bedeuten im einzelnen folgendes:
Gültigkeit: 680x0
Motorola hat zwar ab dem 68030 die PMMU in den Prozessor integriert,
diese aber nur mit einer Funktionsuntermenge der externen PMMU 68851
ausgestattet. AS sperrt bei aktiviertem PMMU-Befehlssatz (s.o.)
deshalb alle fehlenden Befehle, wenn als Zielprozessor 68030 oder
höher eingestellt wurde. Nun kann es aber sein, daß in
einem System mit 68030-Prozessor die interne MMU abgeschaltet wurde
und der Prozessor mit einer externen 68851 betrieben wird. Mit
FULLPMMU ON kann man AS dann mitteilen, daß der
vollständige MMU-Befehlssatz zugelassen ist. Umgekehrt kann man,
wenn man portablen Code erzeugen will, alle zusätzlichen Befehle
trotz 68020-Zielplattform mit FULLPMMU OFF abschalten. Die
Umschaltung darf beliebig oft erfolgen, die momentane Einstellung
kann aus einem gleichnamigen Symbol ausgelesen werden.
ACHTUNG! Der CPU-Befehl besetzt für 680x0-Argumente
implizit diese Einstellung vor! FULLPMMU muß also auf
jeden Fall nach dem CPU-Befehl kommen!
Gültigkeit: 680x0, M*Core, XA, H8, SH7000, TMS9900, MSP430,
ST7
Prozessoren der 680x0-Familie stehen ungeraden Adressen ziemlich
kritisch gegenüber: Befehle dürfen nicht auf einer
ungeraden Adresse beginnen, und Datenzugriffe sind mit ungeraden
Adressen bis zum 68010 nur byteorientiert erlaubt. Die H8-Familie
setzt bei Zugriffen auf ungerade Adressen das unterste Adreßbit
einfach ganz auf Null, die 500er ,,bedanken'' sich wiederum mit einer
Exception... AS bemüht sich daher, mit DC oder
DS angelegte Datenstrukturen immer mit einer geraden Bytezahl
abzulegen. Das bedeutet bei den Befehlen DS.B und
DC.B aber unter Umständen, daß ein Füllbyte
eingefügt werden muß. Dieses Verhalten kann man mit
dem PADDING-Befehl ein- und ausschalten. Als Argument ist
analog zu den vorherigen Befehlen ON oder OFF
erlaubt, und die augenblickliche Einstellung kann aus dem
gleichnamigen Symbol ausgelesen werden. Defaultmäßig
ist PADDING nur für die 680x0-Familie eingeschaltet,
für alle anderen werden erst nach Umschaltung Padding-Bytes
eingefügt!
Gültigkeit: TLCS-900, H8
Die Prozessoren der TLCS-900-Reihe können in 2 Betriebsarten
arbeiten, dem Minimum-und Maximum-Modus. Je nach momentaner
Betriebsart gelten für den Betrieb und den Assembler etwas
andere Eckwerte. Mit diesem Befehl und den Parametern ON
oder OFF teilt man AS mit, daß der folgende Code im
Maximum- oder Minimum-Modus abläuft. Die momentane Einstellung
kann aus der Variablen INMAXMODE ausgelesen werden.
Voreinstellung ist OFF, d.h. Minimum-Modus.
Analog dazu teilt man im H8-Modus AS mit diesem Befehl mit, ob mit
einem 64K- oder 16Mbyte-Adreßraum gearbeitet wird. Für den
einfachen 300er ist diese Einstellung immer OFF und kann
nicht verändert werden.
Gültigkeit: Z380
Der Z380 kann in insgesamt 4 Betriebsarten arbeiten, die sich durch
die Einstellung von 2 Flags ergeben: Das XM-Flag bestimmt, ob der
Prozessor mit einem 64 Kbyte oder 4 Gbyte großen
Adreßraum arbeiten soll und kann nur gesetzt werden (nach einem
Reset steht es Z80-kompatibel auf 0). Demgegenüber legt das
LW-Flag fest, ob Wort-Befehle mit einer Wortlänge von 16 oder 32
Bit arbeiten sollen. Die Stellung dieser beiden Flags
beeinflußt Wertebereichseinschränkungen von Konstanten
oder Adressen, weshalb man AS über diese beiden Befehle deren
Stellung mitteilen muß. Als Default nimmt AS an, daß
beide Flags auf 0 stehen, die momentane Einstellung (ON
oder OFF) kann aus den vordefinierten Variablen
INEXTMODE bzw. INLWORDMODE ausgelesen werden.
Gültigkeit: MCS-251
Intel hat den Befehlssatz der 8051er beim 80C251 deutlich erweitert,
hatte aber leider nur noch einen einzigen freien Opcode für
diese Befehle frei. Damit der Prozessor nicht auf alle Ewigkeit durch
einen Präfix behindert bleibt, hat Intel zwei Betriebsarten
vorgesehen: Den Binär- und den Quellmodus. Im Binärmodus
ist der Prozessor voll 8051-kompatibel, alle erweiterten Befehle
benötigen den noch freien Opcode als Präfix. Im Quellmodus
tauschen diese neuen Befehle ihre Position in der Code-Tabelle mit
den entsprechenden 8051-Instruktionen, welche dann wiederum mit einem
Präfix versehen werden müssen. Damit AS weiß, wann er
Präfixe setzen muß und wann nicht, muß man ihm mit
diesem Befehl mitteilen, ob der Prozessor im Quellmodus (ON)
oder Binärmodus (OFF) betrieben wird. Die momentane
Einstellung kann man aus der Variablen INSRCMODE auslesen.
Der Default ist OFF.
Gültigkeit: MCS-51/251, PowerPC
Bei den Prozessoren der 8051-Serie ist Intel seinen eigenen
Prinzipien untreu geworden: Der Prozessor verwendet entgegen
jeglicher Tradition eine Big-Endian-Orientierung von Mehrbytewerten!
Während dies bei den MCS-51-Prozessoren noch nicht
großartig auffiel, da der Prozessor ohnehin nur 8-bittig auf
Speicherzellen zugreifen konnte, man sich die Byte-Anordnung bei
eigenen Datenstrukturen also aussuchen konnte, ist dies beim MCS-251
nicht mehr so, er kann auch ganze (Lang-)Worte aus dem Speicher lesen
und erwartet dabei das MSB zuerst. Da dies nicht der bisherigen
Arbeitsweise von AS bei der Konstantenablage entspricht, kann man nun
mit diesem Befehl umschalten, ob die Befehle DB, DW, DD, DQ
und DT mit Big- oder Little-Endian-Orientierung arbeiten
sollen. Mit BIGENDIAN OFF (Voreinstellung) wird wie bei
älteren AS-Versionen zuerst das niederwertigste Byte abgelegt,
mit BIGENDIAN ON wird die MCS-251-kompatible Variante
benutzt. Natürlich kann man diese Einstellung beliebig oft im
Code ändern; die momentane Einstellung kann aus dem
gleichnamigen Symbol ausgelesen werden.
Gültigkeit: alle Prozessoren
Bestimmte Mikrokontroller und Signalprozessoren kennen mehrere
Adreßbereiche, die nicht miteinander mischbar sind und jeweils
auch verschiedene Befehle zur Ansprache benötigen. Um auch diese
verwalten zu können, stellt der Assembler mehrere
Programmzähler zur Verfügung, zwischen denen mit dem
SEGMENT-Befehl hin-und hergeschaltet werden kann. Dies erlaubt
es, sowohl in mit INCLUDE eingebundenen Unterprogrammen als
auch im Hauptprogramm benötigte Daten an der Stelle zu
definieren, an denen sie benutzt werden. Im einzelnen werden folgende
Segmente mit folgenden Namen verwaltet:
1.1. Lizenzbedingungen
Zu erreichen bin ich folgendermaßen:
Wer mir persönlich Fragen stellen will (und in der Nähe von
Aachen wohnt), kann dies mit hoher Wahrscheinlichkeit donnerstags von
19.00 bis 21.00 Uhr im Computerclub an der RWTH Aachen
(Eilfschornsteinstraße 16, Keller Philosophengebäude
Rückseite).
ftp.uni-stuttgart.de
Verzeichnis pub/systems/msdos/programming/as
Die Quellen der C-Version können von folgendem Server geholt
werden:
sunsite.unc.edu
Verzeichnis pub/Linux/devel/lang/assemblers/asl-<version>.tar.gz
...und damit natürlich von jedem Sunsite-Spiegel der Welt!
1.2. allgemeine Fähigkeiten des Assemblers
in Arbeit / Planung / Überlegung :
Noch gesucht werden Unterlagen für:
ungeliebt, aber doch vorhanden :
Die Umschaltung des Codegenerators darf dabei auch mitten in der
Datei erfolgen, und das beliebig oft!
Kirk: Analysis, Mr. Spock?
Spock: Captain, it doesn't appear in the symbol table.
Kirk: Then it's of external origin?
Spock: Affirmative.
Kirk: Mr. Sulu, go to pass two.
Sulu: Aye aye, sir, going to pass two.
Datei
Funktion
AS.EXE
AS.OVR
AS.DOC
PLIST.EXE
BIND.EXE
P2HEX.EXE
P2BIN.EXE
AS2MSG.EXEAssembler
Overlay zum Assembler
diese Datei mit der Anleitung
Listet Inhalt von Codedateien auf
kopiert Codedateien zusammen
wandelt Code- in Hexdateien um
wandelt Code- in Binärdateien um
Fehlerfilter AS --> Borland-Pascal
80C50X.INC
80C552.INC
H8_3048.INC
STDDEF04.INC
STDDEF16.INC
STDDEF17.INC
STDDEF18.INC
STDDEF2X.INC
STDDEF37.INC
STDDEF3X.INC
STDDEF47.INC
STDDEF51.INC
STDDEF56.INC
STDDEF5X.INC
STDDEF60.INC
STDDEF62.INC
STDDEF75.INC
STDDEF87.INC
STDDEF90.INC
STDDEF96.INC
STDDEFXA.INC
STDDEFZ8.INC
REG166.INC
REG251.INC
REG29K.INC
REG53X.INC
REG683XX.INC
REG7000.INCRegisteradressen SAB C50x
Registeradressen 80C552
Registeradressen H8/3048
Registeradressen 6804
Befehlsmakros und Registeradressen
PIC16C5x
Registeradressen PIC17C4x
Registeradressen PIC16C8x
Registeradressen TMS3202x
Register- & Bitadressen TMS370xxx
Peripherieadressen TMS320C3x
Befehlsmakros TLCS-47
Definition von SFRs und Bits für
8051/8052/80515
Registeradressen DSP56000
Peripherieadressen TMS320C5x
Befehlsmakros & Registeradressen
PowerPC
Registeradressen & Makros ST6
Registeradressen 75K0
Register- & Speicheradressen TLCS-870
Register- & Speicheradressen TLCS-90
Register- & Speicheradressen TLCS-900
SFR-& Bitadressen Philips XA
Registeradressen Z8-Familie
Adressen & Befehlsmakros 80C166/167
Adressen & Bits 80C251
Peripherieadressen AMD 2924x
Registeradressen H8/53x
Registeradressen 68332/68340/68360
Registeradressen TMS70Cxx
Datei
Funktion
REG78K0.INC
REG96.INC
REGAVR.INC
REGCOP8.INC
REGHC12.INC
REGM16C.INC
REGMSP.INC
REGST9.INC
REGZ380.INCRegister- und Speicheradressen 78K0
Registeradressen MCS-96
Registeradressen Atmel AVR
Registeradressen COP8
Registeradressen Motorola 68HC12...
Registeradressen Mitsubishi M16C
Befehlsmakros & Adressen MSP430
Registeradressen ST9
On-Chip-Register Z380
CTYPE.INC
BITFUNCS.INC
Standardfunktionen zur
Zeichenanalyse
Standardfunktionen zur
Bitmanipulation
DEMOCODE.ASM
DEMOMAC.ASM
DEMOPHAS.ASM
DEMOLIST.ASMBeispielprogramme für die Benutzung
des Assemblers
Datei
Funktion
ASX.EXE
DPMIUSER.DOC
DPMI16BI.OVL
DPMILOAD.EXE
RTM.EXE
RTMRES.EXE
DPMINST.EXE
Protected-Mode-Assembler
Hinweise zur Benutzung
des DPMI-Servers
DPMI-Server für den Assembler
Lader für den DPMI-Server
Run-Time-Modul des Assemblers
DPMI-Server resident laden
DPMI-Server auf den
Rechner installieren
Datei
Funktion
AS2.EXE
PLIST2.EXE
BIND2.EXE
P2HEX2.EXE
P2BIN2.EXEAssembler, OS/2-Version
Dienstprogramme zu AS2; Funktion
analog zu den MSDOS-Versionen
c:\as
c:\as\bin
c:\as\include
c:\as\lib
c:\as\doc
c:\as\demos
Als erstes kopieren Sie alle EXE- sowie OVR-Dateien aus dem Archiv in
das bin-Verzeichnis; um dieses Verzeichnis ergänzen Sie
dann auch die PATH-Anweisung in Ihrer AUTOEXEC.BAT.
In das include-Verzeichnis kommen alle INC-Dateien. In
dem lib-Verzeichnis erzeugen Sie eine Datei AS.RC
mit folgendem Inhalt:
-i c:\as\include
Diese sogenannte Key-Datei zeigt AS, in welchem Verzeichnis er seine
Include-Dateien suchen soll. Damit AS diese Key-Datei bei Start auch
beachtet, muß noch folgende Anweisung in die
AUTOEXEC.BAT:
set ASCMD=@c:\as\lib\as.rc
Was Sie alles noch in der Key-Datei voreinstellen können, steht
im folgenden Abschnitt. Zuguterletzt wandern noch alle DOC-Dateien
ins gleichnamige Verzeichnis sowie die Demo-Assemblerquellen ins
demos-Verzeichnis. Das war's auch schon!
machine not in database (run DPMIINST)
Da bei der Programmausführung ständig zwischen Real und
Protected Mode hin- und hergeschaltet wird, ist es sehr wichtig, die
effizienteste Möglichkeit dafür zu finden. Zu diesem Zweck
(und nur zu diesen) müssen Sie einmal das Programm DPMIINST
starten. Da DPMIINST alleiniger Herr über den Protected Mode
sein möchte, müssen Sie für die Installation
wahrscheinlich einen installierten HIMEM-Treiber herausnehmen (nach
dem Lauf darf HIMEM aber wieder hinein). Folgen Sie einfach den
Anweisungen, die DPMIINST ausgibt. Nach dieser Prozedur
benötigen Sie DPMIINST nicht mehr, sondern nur noch folgende
Programme:
2.4. Aufruf, Parameter
SET USEXMS=n
die Verwendung von extended memory verhindern.
SET ASXSWAP=<Größe>[,Dateiname]
Die Größenangabe erfolgt in Megabytes und muß
gemacht werden. Der Name der Datei ist dagegen optional; fehlt er, so
wird die Swap-Datei im aktuellen Verzeichnis unter dem Namen
ASX.TMP angelegt. In jedem Falle wird die Swap-Datei nach
Programmende wieder gelöscht.
Wird keine Datei gefunden, oder ist die gefundene Nachrichtendatei
inkompatibel zur Version von AS, wird die Programmausführung
abgebrochen.
Schalterparameter erkennt AS daran, daß sie durch einen
Schrägstrich (/) oder Bindestrich (-) eingeleitet werden. Es
gibt dabei sowohl Schalter, die nur aus einem Buchstaben bestehen,
als auch Schalter, die aus einem ganzen Wort bestehen. Immer wenn AS
einen Schalter nicht als ,,Wort-Schalter'' verstehen kann, so
versucht er, die Buchstaben des Wortes als einzelne Schalter zu
interpretieren. Wenn man also z.B.
-queit
anstelle von
-quiet
geschrieben hätte, würde AS die Buchstaben q, u, e,
i und t als einzelne Schalter auffassen.
Mehrbuchstabige Schalter unterscheiden sich weiterhin von
einbuchstabigen dadurch, daß AS bei ihnen beliebige
Groß-und Kleinschreibungen akzeptiert, während
einbuchstabige Schalter je nach Groß- oder Kleinschreibung
unterschiedliche Bedeutung haben.
Zu Sinn und Funktion der SHARED-Symbole siehe Kapitel 2.11 bzw. 3.8.1.
Sofern Schalter keine Argumente benötigen und ihre
Zusammenziehung keinen mehrbuchstabigen Schalter ergibt, können
mehrere Schalter auch auf einen Rutsch angegeben werden, wie z.B im
folgenden Beispiel:
definiert den Prozessortyp <neu> als einen Alias
für den Typen <alt>. Zu den Sinn und Zweck
von Aliassen siehe Abschnitt 2.12
as test*.asm firstprog -cl /i c:\as\8051\include
Es werden alle Dateien TEST*.ASM sowie die Datei FIRSTPROG.ASM
assembliert, wobei für alle Dateien Listings auf der Konsole
ausgegeben und Sharefiles im C-Format erzeugt werden. Nach Includes
soll der Assembler zusätzlich im Verzeichnis C:\AS\8051\INCLUDE
suchen.
as -g test.asm
Die Lösung wäre in diesem Fall, die -g-Option ans
Ende der Kommandozeile zu setzen oder ein explizites
MAP-Argument zu spezifizieren.
set ASCMD=-L -i c:\as\8051\include
eine Menge Tipparbeit ersparen. Da die Environment-Optionen vor der
Kommandozeile abgearbeitet werden, können Optionen in der
Kommandozeile widersprechende im Environment übersteuern.
set ASCMD=@c:\as\as.key
Um Optionen in der ASCMD-Variablen (oder der Key-Datei) wieder
aufzuheben, kann die Option mit einem vorangestellten Pluszeichen
wieder aufgehoben werden. Soll in einem Einzelfall z.B. doch kein
Listing erzeugt werden, so kann es mit
as +L <Datei>
wieder aufgehoben werden. Natürlich ist es nicht ganz logisch,
eine Option mit einem Pluszeichen zu negieren...UNIX soit qui mal y
pense.
/~I --> /i
-#u --> -U
511 KByte verfügbarer Restspeicher
bedeutet also nicht einen nahenden Systemabsturz wegen
Speichermangel, sondern stellt nur den Abstand zu der Grenze dar, bei
der OS/2 einfach ein paar mehr Kohlen in den Ofen schaufelt...
2.5. Format der Eingabedateien
[Label[:]]<Befehl>[.Attribut] [Parameter[,Parameter..]] [;Kommentar]
Der Doppelpunkt nach dem Label ist optional, falls das Label in der
ersten Spalte beginnt (woraus folgt, daß der Befehl niemals in
Spalte 1 beginnen darf). Man muß ihn aber setzen, falls das
Label nicht in der ersten Spalte beginnt, damit AS es von einem
Befehl unterscheiden kann. In letzterem Fall muß übrigens
zwischen Doppelpunkt und dem Befehl mindestens ein Leerzeichen
stehen, falls der eingestellte Zielprozessor zu denjenigen
gehört, bei denen das Attribut auch eine mit einem Doppelpunkt
abgetrennte Formatangabe sein darf. Dieser Knopf ist aus
Eindeutigkeitsgründen nötig, da sonst keine Unterscheidung
zwischen Befehl mit Format und Label mit Befehl möglich
wäre.
Attribut
arithmetisch-logischer Befehl
Sprungbefehl
B
W
L
Q
S
D
X
PByte (8 Bit)
Wort (16 Bit)
Langwort (32 Bit)
Vierfachwort (64 Bit)
Single Precision (32 Bit)
Double Precision (64 Bit)
Extended Precision (80/96 Bit)
Dezimalgleitkomma (80/96 Bit)---------
---------
16-Bit-Displacement
---------
8-Bit-Displacement
---------
32-Bit-Displacement
---------
add.w:g rw10,rw8
Was dieses Beispiel nicht zeigt, ist, daß die Formatangabe auch
ohne Operandengröße geschrieben werden darf. Steht
demgegenüber eine Operandengröße ohne Formatangabe,
verwendet AS automatisch das kürzeste Format. Die erlaubten
Befehlsformate und Operandengrößen sind vom
Maschinenbefehl abhängig und können z.B. [88], [14], [30] bzw. [31]
entnommen werden.
Letztere beide werden nur erzeugt, wenn sie durch zusätzliche
Kommandozeilenoptionen angefordert wurden.
[<n>] <Zeile>/<Adresse> <Code> <Quelle>
Im Feld n zeigt AS die Include-Verschachtelungstiefe an. Die
Hauptdatei (die Datei, mit der die Assemblierung begann), hat dabei
die Tiefe 0, von dort aus eingebundene Dateien haben Tiefe 1 usw. Die
Tiefe 0 wird dabei nicht angezeigt.
Bit
Teil
0
1
2
3
4
5
7Quelldatei(en)+erzeugter Code
Symboltabelle
Makroliste
Funktionsliste
Zeilennumerierung
Registersymboltabelle
Zeichentabellenliste
-t <Maske>
werden die in <Maske> gesetzten Bits gelöscht, so
daß die entsprechenden Listing-Teile unterdrückt werden.
Analog lassen sich mit einem Pluszeichen einzelne Teile wieder
einschalten, falls man es in der ASCMD-Variablen
übertrieben hat...will man z.B. nur die Symboltabelle haben, so
reicht
-t 2 .
In der Belegungsliste werden für jedes Segment einzeln die
belegten Bereiche hexadezimal ausgegeben. Handelt es sich bei einem
Bereich um eine einzige Adresse, wird nur diese ausgegeben, ansonsten
erste und letzte Adresse.
Symbol <Symbolname> (=<Wert>,<Datei>/<Zeile>):
Datei <Datei 1>:
<n1>[(m1)] ..... <nk>[(mk)]
.
.
Datei <Datei l>:
<n1>[(m1)] ..... <nk>[(mk)]
Für jedes Symbol wird aufgelistet, in welchen Dateien es in
welchen Zeilen angesprochen wurde. Sollte ein Symbol mehrmals in der
gleichen Zeile benutzt worden sein, so wird dies durch eine in
Klammern gesetzte Anzahl hinter der Zeilennummer angedeutet. Sollte
ein Symbol niemals benutzt worden sein, erscheint es auch nicht in
der Liste; entsprechend erscheint eine Datei auch überhaupt
nicht in der Liste eines Symbols, falls es in der entsprechenden
Datei nicht referenziert wurde.
2.7. Symbolkonventionen
Name
Bedeutung
TRUE
FALSE
CONSTPI
VERSION
ARCHITECTURE
DATE
TIME
MOMCPU
MOMCPUNAME
MOMFILE
MOMLINE
MOMPASS
MOMSECTION
MOMSEGMENT
*, $ bzw. PClogisch ,,wahr''
logisch ,,falsch''
Kreiszahl Pi (3.1415.....)
Version von AS in BCD-Kodierung,
z.B. 1331 hex für Version 1.33p1
Zielplattform, für die AS übersetzt wurde,
in der Form Prozesor-Hersteller-Betriebssystem
Datum und
Zeitpunkt der Assemblierung (Beginn)
momentan gesetzte Ziel-CPU
dito, nur als voll ausgeschriebener String
augenblickliche Quelldatei
Zeilennummer in Quelldatei
Nummer das laufenden Durchgangs
Name der aktuellen Sektion oder
Leerstring
Name des mit SEGMENT gewählten
Adreßraumes
mom. Programmzähler
cnt set cnt+1
temp equ "\{CNT}"
jnz skip{temp}
.
.
skip{temp}: nop
ACHTUNG! Der Programmierer ist selber dafür
verantwortlich, daß sich dabei gültige Symbolnamen
ergeben!
Label:
.
.
Attr equ symtype(Label) ; ergibt 1
Den einzelnen Segmenttypen sind die in Tabelle 2.8 aufgelisteten Nummern zugeordnet. Die
aus der Ordnung normaler Symbole etwas herausfallenden
Registersymbole sind näher in Abschnitt 2.10 erläutert. Mit einem
undefinierten Symbol als Argument liefert die
SYMTYPE-Funktion -1 als Ergebnis.
Segment
Rückgabewert
<keines>
CODE
DATA
IDATA
XDATA
YDATA
BITDATA
IO
REG
ROMDATA
<Registersymbol>0
1
2
3
4
5
6
7
8
9
1282.8.1. Integerkonstanten
Intel-Modus
(Intel, Zilog,
Thomson, Texas,
Toshiba, NEC,
Siemens, Philips)Motorola-Modus
(Rockwell, Motorola,
Microchip, Thomson,
Hitachi, Atmel)
C-Modus
(PowerPC,
AMD29K,
National,
Symbios)
dezimal
hexadezimal
binär
oktaldirekt
nachgestelltes H
nachgestelltes B
nachgestelltes Odirekt
vorangestelltes $
vorangestelltes %
vorangestelltes @direkt
vorangestelltes 0x
vorangestelltes 0b
vorangestellte 0
'A' ==$41
'AB' ==$4142
'ABCD' ==$41424344
Wichtig ist, daß hier die Zeichen in einfachen
Hochkommas geschrieben werden, um sie von den weiter unten
beschriebenen Stringkonstanten zu unterscheiden.
[-]<Vorkommastellen>[.Nachkommastellen][E[-]Exponent]
lautet. ACHTUNG! Der Assembler versucht eine Konstante zuerst
als Integerkonstante zu verstehen und macht erst dann einen Versuch
mit Gleitkomma, falls dies gescheitert ist. Will man aus
irgendwelchen Gründen die Auswertung als Gleitkommazahl
erzwingen, so kann man dies durch Dummy-Nachkommastellen erreichen,
z.B. 2.0 anstelle 2.
Die Kennbuchstaben dürfen sowohl groß als auch klein
geschrieben werden.
\b : Backspace \a : Klingel \e : Escape \t : Tabulator \n : Zeilenvorschub \r : Wagenrücklauf \\ : Backslash \' oder \h : Hochkomma \" oder \i : Gänsefüßchen
message "Wurzel aus 81 : \{sqrt(81)}"
die Ausgabe
Wurzel aus 81 : 9
Der Assembler wählt anhand des Formelergebnistyps die richtige
Ausgabeform, zu vermeiden sind lediglich weitere Stringkonstanten im
Ausdruck, da der Assembler bei der
Groß-zu-Kleinbuchstabenumwandlung sonst durcheinanderkommt.
move.b #'\n',d0
Jedoch hat alles seine Grenzen, weil der darüberliegende
Splitter, der die Zeile in Opcode und Parameter zerlegt, nicht
weiß, womit er da eigentlich arbeitet, z.B. hier:
move.l #'\'abc',d0
Nach dem dritten Hochkomma findet er das Komma nicht mehr, weil er
vermutet, daß eine weitere Zeichenkonstante beginnt, und eine
Fehlermeldung über eine falsche Parameterzahl ist die Folge.
Abhilfe wäre z.B., \h anstelle \' zu schreiben.
Op.
Funktion
#Ops.
Int
Float
String
Rang
<>
>=
<=
<
>
=
==
!!
||
&&
~~
-
+
#
/
*
^
!
|
&
><
>>
<<
~ Ungleichheit
größer o. gleich
kleiner o. gleich
echt kleiner
echt größer
Gleichheit
Alias für =
log. XOR
log. OR
log. AND
log. NOT
Differenz
Summe
Modulodivision
Quotient
Produkt
Potenz
binäres XOR
binäres OR
binäres AND
Bitspiegelung
log. Rechtsschieben
log. Linksschieben
binäres NOT2
2
2
2
2
2
2
2
2
1
2
2
2
2
2
2
2
2
2
2
2
2
2ja
ja
ja
ja
ja
ja
ja
ja
ja
ja
ja
ja
ja
ja*)
ja
ja
ja
ja
ja
ja
ja
ja
jaja
ja
ja
ja
ja
ja
nein
nein
nein
nein
ja
ja
nein
ja
ja
ja
nein
nein
nein
nein
nein
nein
neinja
ja
ja
ja
ja
ja
nein
nein
nein
nein
nein
ja
nein
nein
nein
nein
nein
nein
nein
nein
nein
nein
nein14
14
14
14
14
14
13
12
11
2
10
10
9
9
9
8
7
6
5
4
3
3
1
*) Rest wird verworfen
Name
Funktion
Argument
Ergebnis
SQRT
SIN
COS
TAN
COT
ASIN
ACOS
ATAN
ACOT
EXP
ALOG
ALD
SINH
COSH
TANH
COTH
LN
LOG
LD
ASINH
ACOSH
ATANH
ACOTH
INT
BITCNT
FIRSTBIT
LASTBIT
BITPOS
SGN
Quadratwurzel
Sinus
Kosinus
Tangens
Kotangens
inverser Sinus
inverser Kosinus
inverser Tangens
inverser Kotangens
Exponentialfunktion
10 hoch Argument
2 hoch Argument
hyp. Sinus
hyp. Kosinus
hyp. Tangens
hyp. Kotangens
nat. Logarithmus
dek. Logarithmus
2er Logarithmus
inv. hyp. Sinus
inv. hyp. Kosinus
inv. hyp. Tangens
inv. hyp. Kotangens
ganzzahliger Anteil
binäre Quersumme
niedrigstes 1-Bit
höchstes 1-Bit
einziges 1-Bit
Vorzeichen (0/1/-1)
arg >= 0
arg in R
arg in R
arg <> (2*n+1)*(Pi)/(2)
arg <> n*Pi
| arg | <= 1
| arg | <= 1
arg in R
arg in R
arg in R
arg in R
arg in R
arg in R
arg in R
arg in R
arg <> 0
arg > 0
arg > 0
arg > 0
arg in R
arg >= 1
| arg | < 1
| arg | > 1
arg in R
Integer
Integer
Integer
Integer
Integer oder
GleitkommaGleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Gleitkomma
Integer
Integer
Integer
Integer
Integer
Integer
Name
Funktion
Argument
Ergebnis
ABS
TOUPPER
TOLOWER
UPSTRING
LOWSTRING
STRLEN
SUBSTR
STRSTR
VAL
Betrag
pass. Großbuchstabe
pass. Kleinbuchstabe
wandelt alle Zeichen
in Großbuchstaben
wandelt alle Zeichen
in Kleinbuchstaben
liefert Länge eines
Strings
extrahiert Teil eines
Strings
sucht Teilstring in
einem String
evaluiert Stringin-
halt als AusdruckInteger oder
Gleitkomma
Integer
Integer
String
String
String
String,
Integer,
Integer
String,
String
String
Integer oder
Gleitkomma
Integer
Integer
String
String
Integer
String
Integer
abh. von
Argument
wur2 equ sqrt(2)
schreiben dürfte --- in solchen Fällen findet automatisch
eine Typkonvertierung statt. Umgekehrt muß allerdings die
INT-Funktion angewandt werden, um eine Gleitkommazahl ganz zu
bekommen. Bei der Benutzung dieser Funktion ist zu beachten,
daß sie als Ergebnis immer einen vorzeichenbehafteten Integer
liefert, sie hat also einen Wertebereich von ca. +/-2.0E9.
2.9. Vorwärtsreferenzen und andere Desaster
move.l d0,#10
loop: move.l d1,(a1)
beq skip
neg.l d1
skip: move.l (a1+),d1
dbra d0,loop
Denkt man sich den Scheifenrumpf mit dem Sprung weg, so bleibt ein
äußerst angenehm zu assemblierendes Programm übrig:
die einzige Referenz ist der Rücksprung zum Anfang des Rumpfes,
und da ein Assembler ein Programm von vorne nach hinten
durcharbeitet, hat er den Symbolwert bereits ermittelt, bevor er ihn
zum erstem Mal benötigt. Sofern man ein Programm hat, das nur
solche Rückwärtsreferenzen besitzt, ist man in der
angenehmen Lage, nur einmal durch den Quellcode gehen zu müssen,
um den korrekten und optimalen Maschinencode zu finden. Einige
Hochsprachen wie Pascal mit ihrer strikten Regel, daß alles vor
der ersten Benutzung definiert sein muß, nutzen genau diese
Eigenschaft aus, um den übersetzungsvorgang zu beschleunigen.
cpu 6811
org $8000
beq skip
rept 60
ldd Var
endm
skip: nop
Var equ $10
Aufgrund der Adreßlage nimmt AS im ersten Pass lange Adressen
für die LDD-Befehle an, was eine Code-Länge von
180 Bytes ergibt und im zweiten Pass (zum Zeitpunkt des
BEQ-Befehls ist noch der ,,falsche'' Wert von skip
aktuell, d.h. AS weiß zu diesem Zeitpunkt noch nicht, daß
der Code in Wirklichkeit nur 120 Bytes lang ist) gibt es eine
Fehlermeldung wegen einer überschrittenen Sprungdistanz. Dieser
Fehler läßt sich auf drei Arten vermeiden:
Noch ein Hinweis zum EQU-Befehl: Da AS nicht wissen kann, in
welchem Zusammenhang ein mit EQU definiertes Symbol
später verwendet wird, wird ein EQU mit
Vorwärtsreferenzen im ersten Pass überhaupt nicht
durchgeführt. Wird das mit EQU definierte Symbol also
im zweiten Pass vorwärts referenziert:
move.l #sym2,d0
sym2 equ sym1+5
sym1 equ 0
so handelt man sich im zweiten Pass eine Fehlermeldung wegen eines
undefinerten Symbols ein...aber warum machen Leute eigentlich solche
Dinge ???
myreg reg r17 ; Definition Registersymbol
addi myreg+1,3 ; geht nicht!
Zum anderen muß ein Registersymbol vor seiner ersten Nutzung
definiert werden; eine Vorwärtsreferenz würde dazu
führen, daß AS bei nicht gefundenem Registersymbol eine
Vorwärtsreferenz auf eine Speicherstelle vermutet, und bei den
meisten Prozessoren sind die Nutzungsmöglichkeiten für
Speicherstellen als Operanden deutlich eingeschränkter als
für Register, so daß es mit ziemlicher Sicherheit Fehler
hagelt...
2.12. Prozessor-Aliasse
VecCnt SET 0 ; irgendwo am Anfang
...
DefVec MACRO Name ; einen neuen Vektor belegen
Name EQU VecCnt
VecCnt SET VecCnt+4
ENDM
...
DefVec Vec1 ; ergibt Vec1=0
DefVec Vec2 ; ergibt Vec2=4
Intern werden Konstanten und Variablen identisch gespeichert, der
einzige Unterschied ist, daß sie mit SET umdefiniert
werden können und mit EQU nicht. Es ist daher
möglich, ein Symbol mit EQU zu definieren und es
mit SET zu ändern, auch wenn das nicht der Sinn der
Sache ist. Aus einem weiteren Grund sollte man davon sogar explizit
die Finger lassen: Im Gegensatz zu SET prüft
EQU, ob der neu zugewiesene Wert sich von einem evtl. bisher
existierenden unterscheidet. Da sich dieser für mit EQU
definierte Konstanten nicht ändern sollte, vermutet AS einen
Phasenfehler und legt einen weiteren Pass ein...würde man in
obigem Beispiel z.B. die Initialisierung des Zählers mit
EQU durchführen, so würde AS sich zwar nicht
beschweren, da eine einmalige Neuzuweisung pro Pass erlaubt ist (bei
n Durchgängen kommt man nun einmal n-mal an dieser Stelle
vorbei), aber endlos neue Passes anstoßen, da der Initialwert
des Zählers immer vom Endwert verschieden ist.
IntZwei EQU 2
FloatZwei EQU 2.0
Einige Prozessoren besitzen leider bereits selber einen
SET-Befehl. Bei diesen muß EVAL anstelle von
SET verwendet werden.
PSW SFR 0d0h ; ergibt PSW = D0H (Datensegment)
PSW SFRB 0d0h ; zusaetzlich PSW.0 = D0H (Bit)
; bis PSW.7 = D7H (Bit)
Da beim 80C251 grundsätzlich alle SFRs ohne zusätzliche
Bit-Symbole bitadressierbar sind, ist der SFRB-Befehl
für ihn auch nicht mehr definiert; die Bits PSW.0
bis PSW.7 sind automatisch vorhanden.
<Name> label $
erzeugt aber ein Symbol mit korrekten Attributen.
Mein_Carry bit PSW.7
auf einem 8051 noch dem Symbol Mein_Carry den Wert 0d7h
zuweisen würde, würde auf einem 80C251 dagegen ein Wert von
070000d0h generiert werden, d.h. die Adresse steht in Bit 0..7 sowie
die Bitstelle in Bit 24..26. Dieses Verfahren entspricht dem, das
auch beim DBIT- Befehl des TMS370 angewendet wird und funktioniert
sinngemäß so auch beim 80C166, nur daß dort
Bitstellen von 0 bis 15 reichen dürfen:
MSB BIT r5.15
Beim Philips XA findet sich in Bit 0..9 die Bitadresse, wie sie auch
in die Maschinenbefehle eingesetzt wird, für Bits aus den
RAM-Speicher wird in Bit 16..23 die 64K-Bank eingesetzt.
bit1 BIT @h+5.2
erlaubt.
invbit BIT r6.!3
Näheres zum BIT-Befehl beim ST9 findet sich bei den
prozessorspezifischen Hinweisen.
INT3 EQU P019
INT3_ENABLE DBIT 0,INT3
das Bit, welches Interrupts von Anschluß INT3 freigibt. So
definierte Bits können dann von den Befehlen SBIT0, SBIT1,
CMPBIT, JBIT0 und JBIT genutzt werden.
PIO_Port_A PORT 20h
PIO_Port_B PORT PIO_Port_A+1
PIO_Port_C PORT PIO_Port_A+2
PIO_Ctrl PORT PIO_Port_A+3
CHARSET 'ä',128
daß das Zielsystem das ä mit der Zahl 128 kodiert. Sind
jedoch zwei weitere Integers angegeben, so ist der erste von ihnen
der letzte zu modifizierende Eintrag, der zweite der neue Wert des
ersten Eintrags; alle weiteren Einträge bis zum Bereichsende
werden sequentiell neu belegt. Falls z.B. das Zielsystem keine
Kleinbuchstaben unterstützt, können mit
CHARSET 'a','z','A'
alle Kleinbuchstaben auf die passenden Großbuchstaben
automatisch umgemappt werden.
CHARSET 'a',"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
ENUM SymA,SymB,SymC
Dieser Befehl weist den Symbolen SymA, SymB und
SymC die Werte 0, 1 und 2 zu.
ENUM Januar=1,Februar,März,April,Mai,Juni
Hier werden den Monatsnamen die Zahlenwerte 1..6 zugewiesen.
Möchte man die Aufzählung nun fortsetzen, geht das
folgendermaßen:
ENUM Juli=Juni+1,August,September,Oktober
Die Definition von Symbolen mit ENUM gleicht einer
Definition mit EQU, d.h. es ist nicht möglich, einem
Symbol einen neuen Wert zuzuweisen.
ENUM November=Oktober+1,Dezember
Rec STRUCT
Ident db ?
Pad db ?
Pointer dd ?
Rec ENDSTRUCT
würde also dem Symbol Rec_len der Wert 6 zugewiesen.
ACHTUNG! Innerhalb einer Strukturdefinition dürfen keine
Befehle verwendet werden, die Code erzeugen, da es sich hier um eine
reine Anordnung von Elementen im Adreßraum handelt!
pushv ,var1,var2,var3
.
.
popv ,var3,var2,var1
AS verwendet dann einen internen, vordefinierten Default-Stack.
Prozessor
CODE
DATA
IDATA
XDATA
YDATA
BITDATA
IO
REG
ROMDATA
68xxx
4G
---
---
---
---
---
---
---
---
DSP56000/
DSP5630064K/
16M---
---
64K/
16M64K/
16M---
---
---
---
PowerPC
4G
---
---
---
---
---
---
---
---
M*Core
4G
---
---
---
---
---
---
---
---
6800,6301,
6811,64K
---
---
---
---
---
---
---
---
6805/HC08
8K
---
---
---
---
---
---
---
---
6809,
630964K
---
---
---
---
---
---
---
---
68HC12
64K
---
---
---
---
---
---
---
---
68HC16
1M
---
---
---
---
---
---
---
---
H8/300
H8/300H64K
16M---
---
---
---
---
---
---
---
H8/500
(Min)
H8/500
(Max)64K
16M
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
SH7000/
7600/77004G
---
---
---
---
---
---
---
---
6502,
MELPS74064K
---
---
---
---
---
---
---
---
65816,
MELPS
770016M
---
---
---
---
---
---
---
---
MELPS
45008K
416
---
---
---
---
---
---
---
M16
4G
---
---
---
---
---
---
---
---
M16C
1M
---
---
---
---
---
---
---
---
4004
4K
256
---
---
---
---
---
---
---
Prozessor
CODE
DATA
IDATA
XDATA
YDATA
BITDATA
IO
REG
ROMDATA
MCS-48,
MCS-41
MCS-51
4K
64K
---
256
256
256 *
In. 80H256
64K
---
---
---
256
---
---
---
---
---
---
MCS-251
16M
---
---
---
---
---
512
---
---
MCS-(1)96
196N/29664K
16M---
---
---
---
---
---
---
---
8080,
64K
---
---
---
---
---
256
---
---
80x86,
64K
64K
---
64K
---
---
64K
---
---
68xx0
4G
---
---
---
---
---
---
---
---
8X30x
8K
---
---
---
---
---
---
---
---
XA
16M
16M
---
---
---
---
2K
In. 1K---
---
AVR
8K
64K
---
---
---
---
64
---
---
29XXX
4G
---
---
---
---
---
---
---
---
80C166,
80C167256K
16M---
---
---
---
---
---
---
---
Z80,
Z180,
Z38064K
512K +
4G---
---
---
---
---
256
256
4G---
---
Z8
64K
256
---
64K
---
---
---
---
---
TLCS-
900(L)16M
---
---
---
---
---
---
---
---
TLCS-90
64K
---
---
---
---
---
---
---
---
TLCS-
87064K
---
---
---
---
---
---
---
---
TLCS-47
64K
1K
---
---
---
---
16
---
---
TLCS-
900016M
---
---
---
---
---
---
---
---
PIC
16C5x2K
32
---
---
---
---
---
---
---
* Da der 8051 kein RAM jenseits 80h hat, muß der Initialwert für den 8051
als Zielprozessor auf jeden Fall mit ORG angepaßt werden!!
+ Da der Z180 weiterhin logisch nur 64K ansprechen kann, ist der
ganze Adreßraum nur mittels PHASE-Anweisungen erreichbar!
Prozessor
CODE
DATA
IDATA
XDATA
YDATA
BITDATA
IO
REG
ROMDATA
PIC
16C5x
PIC
16C64,
16C862K
8K
32
512
---
---
---
---
---
---
---
---
---
---
---
---
---
---
PIC
17C4264K
256
---
---
---
---
---
---
---
ST6
4K
256
---
---
---
---
---
---
---
ST7
64K
---
---
---
---
---
---
---
---
ST9
64K
64K
---
---
---
---
---
256
---
6804
4K
256
---
---
---
---
---
---
---
32010
320154K
4K144
256---
---
---
---
8
8---
---
320C2x
64K
64K
---
---
---
---
16
---
---
320C3x
16M
---
---
---
---
---
---
---
---
320C5x
64K
64K
---
---
---
---
64K
---
---
TMS
990064K
---
---
---
---
---
---
---
---
TMS
70Cxx64K
---
---
---
---
---
---
---
---
370xxx
64K
---
---
---
---
---
---
---
---
MSP430
64K
---
---
---
---
---
---
---
---
SC/MP
64K
---
---
---
---
---
---
---
---
COP8
8K
256
---
---
---
---
---
---
---
µPD
78(C)1064K
---
---
---
---
---
---
---
---
75K0
16K
4K
---
---
---
---
---
---
---
78K0
64K
---
---
---
---
---
---
---
---
7720
512
128
---
---
---
---
---
---
512
Prozessor
CODE
DATA
IDATA
XDATA
YDATA
BITDATA
IO
REG
ROMDATA
7725
2K
256
---
---
---
---
---
---
1024
77230
8K
---
---
512
512
---
---
---
1K
53C8XX
4G
---
---
---
---
---
---
---
---
In dieser Familie liegen die Unterschiede in hinzukommenden Befehlen
und Adressierungsarten (ab 68020). Eine kleine Ausnahme stellt der
Schritt zum 68030 dar, dem 2 Befehle fehlen: CALLM und
RTM. Die drei Vertreter der 683xx-Famile haben den gleichen
Prozessorkern (eine leicht abgemagerte 68020-CPU), jedoch völlig
unterschiedliche Peripherie. MCF5200 repräsentiert die
ColdFire-Familie von Motorola, zum 680x0 binär
abwärtskompatible RISC-Prozesoren. Beim 68040 kommen die
zusätzlichen Steuerregister (via MOVEC erreichbar)
für On-Chip-MMU und Caches sowie einige Systembefehle für
selbige hinzu.
a) 68008 -> 68000 -> 68010 -> 68012 -> MCF5200 -> 68332 -> 68340 -> 68360 -> 68020 -> 68030 -> 68040
b) 56000 --> 56002 --> 56300
Während der 56002 nur Befehle zum Inkrementieren und
Dekrementieren der Akkus ergänzt, ist der 56300-Kern schon fast
ein neuer Prozessor: Er vergrößert alle
Adreßräume von 64K-Wörtern auf 16M und verdoppelt
fast die Anzahl der Befehle.
c) PPC403 --> MPC505 --> MPC601 --> RS6000
Der PCC403 ist eine abgespeckte Version der PowerPC-Linie ohne
Gleitkommaeinheit, demzufolge sind sämtliche Gleitkommabefehle
bei ihm gesperrt; dafür sind einige mikrocontrollerspezifische
Befehle enthalten, die er als einziges Mitglied in dieser Familie
kennt. Der MPC505 (eine Mikrokontroller-Variante mit FPU)
unterscheidet sich solange vom 601er nur in den Peripherieregistern,
wie ich es nicht besser weiß - [44]
hält sich da noch etwas bedeckt... Die RS6000-Reihe kennt noch
einige Befehle mehr (die auf vielen 601er-Systemen emuliert werden,
um vollständige Kompatibilität herzustellen),
außerdem verwendet IBM z.T. andere Mnemonics für diese
reinen Workstation-Prozessoren, als Remineszenz an die
370er-Großrechner...
d) MCORE
e) 6800 -> 6301 -> 6811
Während der 6301 nur neue Befehle definiert, liefert der 6811
neben weiteren Befehlen ein zweites Indexregister Y zur Adressierung.
f) 6809/6309 und 6805/68HC08
Diese Prozessoren sind zwar teilweise quellcodekompatibel zu den
anderen 68xx-ern, haben aber ein anderes Binärcodeformat und
einen deutlich eingeschränkteren (6805) bzw. erweiterten (6809)
Befehlssatz. Der 6309 ist eine CMOS-Version des 6809, die zwar
offiziell nur kompatibel zum 6809 ist, inoffiziell aber mehr Register
und deutlich mehr Befehle besitzt (siehe [24]).
g) 68HC12
h) 68HC16
i) HD6413308 --> HD6413309
Diese beiden Namen repräsentieren die 300er und 300H-Varianten
der H8-Familie; die H-Version besitzt dabei einen größeren
Adreßraum (16 Mbyte statt 64Kbyte), doppelt so breite Register
(32 Bit) und kennt einige zusätzliche Befehle und
Adressierungsarten. Trotzdem ist sie binär
aufwärtskompatibel.
j) HD6475328 --> HD6475348 --> HD6475368 --> HD6475388
Diese Prozessoren besitzen alle den gleichen CPU-Kern; Die unter-
schiedlichen Typen dienen lediglich der Einbindung des korrekten
Registersatzes in der Datei REG53X.INC.
k) SH7000 --> SH7600 --> SH7700
Der Prozessorkern des 7600ers bietet eine Handvoll Befehle mehr, die
Lücken im Befehlssatz des 7000ers schließen
(verzögerte, bedingte sowie relative und indirekte Sprünge,
Multiplikationen mit 32-Bit-Operanden sowie
Multiplizier/Addier-Befehle). Die 7700er-Reihe (auch als SH3
geläufig) bietet weiterhin eine zweite Registerbank, bessere
Schiebebefehle sowie Befehle zur Cache-Steuerung.
l) 6502 -> 65(S)C02 / MELPS740
Die CMOS-Version definiert einige zusätzliche Befehle,
außerdem sind bei einigen Befehlen Adressierungsarten
hinzugekommen, die beim 6502 nicht möglich waren. Die
Mitsubishi-Mikrokontroller dagegen erweitern den 6502-Befehlssatz in
erster Linie um Bitoperationen und Multiplikations-/Divisionsbefehle.
Bis auf den unbedingten Sprung und Befehle zur
Inkrementierung/Dekremetierung des Akkumulatos sind die Erweiterungen
disjunkt. Dem 65SC02 fehlen die Bitmanipulationsbefehle des 65C02.
Mit dem Prozessortyp 6502UNDOC sind die ,,undokumentierten''
6502-Befehle erreichbar, d.h. die Operationen, die sich bei der
Verwendung nicht als Befehle definierter Bitkombinationen im Opcode
ergeben. Die von AS unterstützten Varianten sind im Kapitel mit
den prozessorspezifischen Hinweisen beschrieben.
m) MELPS7700, 65816
Neben einer ,,16-Bit-Version'' des 6502-Befehlssatzes bieten diese
Prozessoren einige Befehlserweiterungen. Diese sind aber
größerenteils disjunkt, da sie sich an ihren jeweiligen
8-bittigen Vorbildern (65C02 bzw. MELPS-740) orientieren. Z.T.~werden
auch andere Mnemonics für gleiche Befehle verwendet.
n) MELPS4500
o) M16
p) M16
q) 4004
r) 8021, 8022, 8039, 80C39, 8048, 80C48, 8041, 8042
Bei den ROM-losen Versionen 8039 und 80C39 sind die Befehle verboten,
die den BUS (Port 0) ansprechen. Der 8021 und 8022 sind
Sonderversionen mit stark abgemagertem Befehlssatz, wofür der
8022 zwei A/D-Wandler und die dazugehörigen Steuerbefehle
enthält. Die CMOS-Versionen lassen sich mit dem
IDL-Befehl in einen Ruhezustand niedriger Stromaufnahme
überführen. Der 8041 und 8042 haben einige Zusatzbefehle
zur Steuerung der Busschnittstelle, dafür fehlen aber einige
andere Befehle. Darüber hinaus ist bei diesen Prozessoren der
Programmadreßraum nicht extern erweiterbar, weshalb AS das
Codesegment bei diesen Prozessoren auf 1 bzw. 2 Kbyte begrenzt.
Der 87C750 kann nur max. 2 Kbyte Programmspeicher adressieren,
weshalb die LCALL- und LJMP-Befehle bei ihm fehlen.
Zwischen den acht mittleren Prozessoren nimmt AS selber
überhaupt keine Unterscheidung vor, sondern verwaltet den
Unterschied lediglich in der Variablen MOMCPU (s.u.), die
man mit IF-Befehlen abfragen kann. Eine Ausnahme stellt
lediglich der 80C504, der in seiner momentanen Form noch einen
Maskenfehler zeigt, wenn eine AJMP- oder
ACALL-Anweisung auf der vorletzten Adresse einer 2K-Seite steht.
AS benutzt in einem solchen Fall automatisch lange Sprungbefehle bzw.
gibt eine Fehlermeldung aus. Der 80C251 hingegen stellt einen
drastischen Fortschritt in Richtung 16/32 Bit, größerer
Adreßräume und orthogonalerem Befehlssatz dar.
s) 87C750 -> 8051, 8052, 80C320, 80C501, 80C502, 80C504, 80515, and 80517 -> 80C251
t) 8096 -> 80196 -> 80196N -> 80296
Neben einem anderen Satz von SFRs (die übrigens von Unterversion
zu Unterversion stark differieren) kennt der 80196 eine Reihe von
zusätzlichen Befehlen und kennt einen ,,Windowing''-Mechanismus,
um das größere interne RAM anzusprechen. Die
80196N-Familie wiederum erweitert den Adreßraum auf 16 Mbyte
und führt eine Reihe von Befehlen ein, mit denen man auf
Adressen jenseits 64 Kbyte zugreifen kann. Der 80296 erweitert den
CPU-Kern um Befehle zur Signalverarbeitung und ein zweites
Windowing-Register, verzichtet jedoch auf den Peripheral
Transaction Server (PTS) und verliert damit wieder zwei
Maschinenbefehle.
u) 8080 und 8085
Der 8085 kennt zusätzlich die Befehle RIM und
SIM zum Steuern der Interruptmaske und der zwei I/O-Pins.
v) 8086 -> 80186 -> V30 -> V35
Hier kommen wieder nur neue Befehle dazu. Die entsprechenden 8-Bitter
sind wegen ihrer Befehlskompatibilität nicht aufgeführt,
für ein 8088-System ist also z.B. 8086 anzugeben.
w) 80960
x) 8X300 -> 8X305
Der 8X305 besitzt eine Reihe zusätzlicher Arbeitsregister, die
dem 8X300 fehlen und kann mit diesen auch zusätzliche
Operationen ausführen, wie das direkte Schreiben von
8-Bit-Werten auf Peripherieadressen.
y) XAG1, XAG2, XAG3
Diese Prozessoren unterscheiden sich nur in der Größe des
eingebauten ROMs, die in STDDEFXA.INC definiert ist.
z) AT90S1200 -> AT90S2313 -> AT90S4414 -> AT90S8515
Der erste Vertreter der AVR-Reihe stellt die Minimalkonfiguration
dar, ohne RAM-Speicher und demzufolge auch ohne Load/Store-Befehle.
Die beiden anderen Prozessoren unterscheiden sich nur im
Speicherausbau und in der eingebauten Peripherie, was in
REGAVR.INC differenziert wird.
aa) AM29245 -> AM29243 -> AM29240 -> AM29000
Je weiter man sich in der Liste nach rechts bewegt, desto weniger
Befehle müssen in Software emuliert werden. Während z.B.
der 29245 noch nicht einmal einen Hardware-Multiplizierer besitzt,
fehlen den beiden Vertretern in der Mitte nur die Gleitkommabefehle.
Der 29000 dient dabei als ,,generischer'' Typ, der alle Befehle in
Hardware versteht.
ab) 80C166 --> 80C167,80C165,80C163
80C167 und 80C165/163 haben anstelle 256 Kbyte max. 16 Mbyte
Adreßraum, außerdem kennen sie einige zusätzliche
Befehle für erweiterte Adressierungsmodi sowie atomare
Befehlssequenzen. Untereinander unterscheiden sich diese Prozessoren
der ,,zweiten Generation'' nur in der eingebauten Peripherie.
ac) Z80 -> Z80UNDOC -> Z180 -> Z380
Während für den Z180 nur die zusätzlichen Befehle
definiert sind (d.h. die Z180-MMU findet noch keine
Berücksichtigung), besitzt der Z380 32-Bit-Register, einen
linearen 4Gbyte-Adreßraum sowie neben einer Reihe von
Befehlserweiterungen, die den Befehlssatz deutlich orthogonaler
machen, neue Adressierungsmodi (Ansprechen der
Indexregisterhälften, Stack-relativ). Zu einem kleinen Teil
existieren diese Erweiterungen aber auch schon beim Z80 als
undokumentierte Befehle, die mit der Variante Z80UNDOC
zugeschaltet werden können. Eine Liste mit den zusätzlichen
Befehlen findet sich im Kapitel mit den prozessorspezifischen
Hinweisen.
ad) Z8601, Z8604, Z8608, Z8630, Z8631
Diese Prozessoren unterscheiden sich wieder nur in Speicherausbau und
Peripherie, d.h. die Wahl hat auf den unterstützten Befehlssatz
keinen Effekt.
ae) 96C141, 93C141
Diese beiden Prozessoren repräsentieren die beiden Varianten der
Prozessorfamilie: TLCS-900 und TLCS-900L. Die Unterschiede dieser
beiden Varianten werden in Abschnitt 4.21 genauer beleuchtet.
af) 90C141
ag) 87C00, 87C20, 87C40, 87C70
Die Prozessoren der TLCS-870-Reihe haben zwar den identischen
CPU-Kern, je nach Variante aber eine unterschiedliche
Peripherieausstattung. Zum Teil liegen Register gleichen Namens auf
unterschiedlichen Adressen. Die Datei STDDEF87.INC benutzt analog zur
MCS-51-Familie die hier mögliche Unterscheidung, um automatisch
den korrekten Symbolsatz bereitzustellen.
ah) 47C00 -> 470C00 -> 470AC00
Diese drei Varianten der TLCS-47-Familie haben unterschiedlich
große RAM-und ROM-Adreßbereiche, wodurch jeweils einige
Befehle zur Bankumschaltung hinzukommen oder wegfallen.
ai) 97C241
aj) 16C54 -> 16C55 -> 16C56 -> 16C57
Diese Prozessoren unterscheiden sich durch den verfügbaren
Adreßraum im Programmspeicher, d.h. durch die Adresse, ab der
der AS Überläufe anmeckert.
ak) 16C64, 16C84
Analog zur MCS-51-Familie findet hier keine Unterscheidung im
Codegenerator statt, die unterschiedlichen Nummern dienen lediglich
der Einblendung der korrekten SFRs in STDDEF18.INC.
al) 17C42
am) ST6210/ST6215 -> ST6220/ST6225
Die einzige Unterscheidung, die AS zwischen den beiden Paaren
vornimmt, ist der bei den ersten beiden kleinere Adreßraum (2K
anstelle 4K). Die Feinunterscheidung dient zur automatischen
Unterscheidung in der Quelldatei, welche Hardware jeweils vorhanden
ist (analog zum 8051/52/515).
an) ST7
ao) ST9020, ST9030, ST9040, ST9050
Diese 4 Namen vetreten die vier ,,Unterfamilien'' der ST9-Familie,
die sich durch eine unterschiedliche Ausstattung mit
On-Chip-Peripherie auszeichen. Im Prozessorkern sind sie identisch,
so daß diese Unterscheidung wieder nur im Includefile mit den
Peripherieadressen zum Zuge kommt.
ap) 6804
aq) 32010 -> 32015
Der TMS32010 besitzt nur 144 Byte internes RAM, weshalb AS Adressen
im Datensegment auf eben diesen Bereich begrenzt. Für den 32015
gilt diese Beschränkung nicht, es kann der volle Bereich von
0--255 angesprochen werden.
ar) 320C25 -> 320C26 -> 320C28
Diese Prozessoren unterscheiden sich nur leicht in der
On-Chip-Peripherie sowie den Konfigurationsbefehlen.
as) 320C30, 320C31
Der 320C31 ist eine etwas ,,abgespeckte'' Version mit dem gleichen
Befehlssatz, jedoch weniger Peripherie. In STDDEF3X.INC wird diese
Unterscheidung ausgenutzt.
at) 320C50, 320C51, 320C53
Die Unterscheidung zwischen diesen Prozessoren wird von AS momentan
nicht ausgenutzt.
au) TMS9900
Alle Mitglieder dieser Familie haben den gleichen CPU-Kern,
unterscheiden sich im Befehlssatz also nicht. Die Unterschiede finden
sich nur in der Datei REG7000.INC, in der Speicherbereiche und
Peripherieadressen definiert werden. Die in einer Zeile stehenden
Typen besitzen jeweils gleiche Peripherie und gleiche interne
RAM-Menge, unterscheiden sich also nur in der Menge eingebauten ROMs.
av) TMS70C00, TMS70C20, TMS70C40, TMS70CT20, TMS70CT40, TMS70C02, TMS70C42, TMS70C82, TMS70C08, TMS70C48
aw) 370C010, 370C020, 370C030, 370C040 und 370C050
Analog zur MCS-51-Familie werden die unterschiedlichen Typen nur zur
Unterscheidung der Peripherie in STDDEF37.INC genutzt, der
Befehlssatz ist identisch.
ax) MSP430
ay) SC/MP
az) COP87L84
Dies ist das momentan einzige unterstützte Mitglied der
COP8-Familie von National Semiconductor. Mir ist bekannt, daß
die Familie wesentlich größer ist und auch Vertreter mit
unterschiedlich großem Befehlssatz existieren, die nach Bedarf
hinzukommen werden. Es ist eben ein Anfang, und die Dokumentation von
National ist ziemlich umfangreich...
ba) 7810 -> 78C10
Die NMOS-Version besitzt keinen STOP-Modus; der entspechende Befehl
sowie das ZCM-Register fehlen demzufolge. VORSICHT! NMOS- und
CMOS-Version differieren zum Teil in den Reset-Werten einiger
Register!
Dieses ,,Füllhorn'' an Prozessoren unterscheidet sich innerhalb
einer Gruppe nur durch die RAM- und ROM-Größe; die Gruppen
untereinander unterscheiden sich einmal durch ihre on-chip-Peripherie
und zum anderen durch die Mächtigkeit des Befehlssatzes.
bb) 75402, 75004, 75006, 75008, 75268, 75304, 75306, 75308, 75312, 75316, 75328, 75104, 75106, 75108, 75112, 75116, 75206, 75208, 75212, 75216, 75512, 75516
bc) 78070
Dies ist das einzige, mir momentan vertraute Mitglied der
78K0-Familie von NEC. Es gelten ähnliche Aussagen wie zur
COP8-Familie!
bd) 7720 -> 7725
Der µPD7725 bietet im Vergleich zu seinem Vorgänger
größere Adreßräume und einige zusätzliche
Befehle. VORSICHT! Die Prozessoren sind nicht zueinander
binärkompatibel!
be) 77230
Die einfacheren Mitglieder dieser Familie von SCSI-Prozessoren
besitzen einige Befehlsvarianten nicht, außerdem unterscheiden
sie sich in ihrem Satz interner Register.
bf) SYM53C810, SYM53C860, SYM53C815, SYM53C825, SYM53C875, SYM53C895
CPU 68010+10
ist also nicht zulässig. Gültige Aufrufe sind z.B.
CPU 8051
oder
CPU 6800
Egal, welcher Prozessortyp gerade eingestellt ist, in der
Integervariablen MOMCPU wird der momentane Status als Hexadezimalzahl
abgelegt. Für den 68010 ist z.B. MOMCPU=$68010,
für den 80C48 MOMCPU=80C48H. Da man Buchstaben
außer A..F nicht als Hexziffer interpretieren kann, muß
man sich diese bei der Hex-Darstellung des Prozessors wegdenken.
Für den Z80 ist z.B. MOMCPU=80H.
myrtd MACRO disp
IF MOMCPU$<$68010 ; auf 68008 und
MOVE.L (sp),disp(sp) ; 68000 emulieren
LEA disp(sp),sp
RTS
ELSEIF
RTD #disp ; ab 68010 direkt
ENDIF ; benutzen
ENDM
CPU 68010
MYRTD 12 ; ergibt RTD #12
CPU 68000
MYRTD 12 ; ergibt MOVE.. /
; LEA.. / RTS
Da nicht alle Prozessornamen nur aus Ziffern und Buchstaben zwischen
A und F bestehen, wird zusätzlich der volle Name in der
String-Variablen MOMCPUNAME abgelegt.
Gültigkeit: 680x0, FPU auch 80x86, i960, SUPMODE auch TLCS-900, SH7000, i960, 29K, XA, PowerPC, M*CORE und TMS9900
Benutzung von auf diese Weise gesperrten Befehlen erzeugt bei
SUPMODE eine Warnung, bei PMMU und FPU eine
echte Fehlermeldung.
Zu Adreßbereich und Initialwerten der Segmente siehe Abschnitt
3.2.1. (ORG). Je nach
Prozessorfamilie sind auch nicht alle Segmenttypen erlaubt.
Das Bitsegment wird so verwaltet, als ob es ein Bytesegment wäre, d.h. die Adressen inkrementieren um 1 pro Bit.
Labels, die in einem Segment eines bestimmten Typs definiert werden, erhalten diesen Typ als Attribut. Damit hat der Assembler eine begrenzte Prüfmöglichkeit, ob mit den falschen Befehlen auf Symbole in einem Segment zugegriffen wird. In solchen Fällen wird der Assembler eine Warnung ausgeben.
Beispiel:
CPU 8051 ; MCS-51-Code SEGMENT code ; Testcodeblock SETB flag ; keine Warnung SETB var ; Warnung : falsches Segment SEGMENT data var DB ? SEGMENT bitdata flag DB ?
Gültigkeit: alle Prozessoren
In manchen Anwendungen (speziell Z80-Systeme) muß Code vor der Benutzung in einen anderen Adreßbereich verschoben werden. Da der Assembler davon aber nichts weiß, würde er alle Labels in dem zu verschiebenden Teil auf die Ladeadressen ausrichten. Der Programmierer müßte Sprünge innerhalb dieses Bereiches entweder lageunabhängig kodieren oder die Verschiebung bei jedem Symbol ,,zu Fuß'' addieren. Ersteres ist bei manchen Prozessoren gar nicht möglich, letzteres sehr fehleranfällig.
Mit dem Befehlen PHASE und DEPHASE ist es möglich, dem Assembler mitzuteilen, auf welcher Adresse der Code im Zielsystem effektiv ablaufen wird:
PHASE <Adresse>informiert den Assembler davon, daß der folgende Code auf der spezifizierten Adresse ablaufen soll. Der Assembler berechnet daraufhin die Differenz zum echten Programmzähler und addiert diese Differenz bei folgenden Operationen dazu:
DEPHASEwieder aufgehoben.
Obwohl dieses Befehlspaar vornehmlich in Codesegmenten Sinn macht, verwaltet der Assembler für alle definierten Segmente Phasenwerte.
Gültigkeit: alle Prozessoren
Mit dem Befehl SAVE legt der Assembler den Inhalt folgender Variablen auf einen internen Stapel:
SAVE ; alten Zustand retten LISTING OFF ; Papier sparen .. ; der eigentliche Code RESTORE ; wiederherstellenGegenüber einem einfachen LISTING OFF..ON-Pärchen wird hier auch dann der korrekte Zustand wieder hergestellt, wenn die Listingerzeugung bereits vorher ausgeschaltet war.
Der Assembler überprüft, ob die Zahl von SAVE-und RESTORE-Befehlen übereinstimmt und liefert in folgenden Fällen Fehlermeldungen:
Gültigkeit: diverse
Mit diesem Befehl kann man AS den aktuellen Stand bestimmter Register mitteilen, deren Inhalt sich nicht mit einem einfachen ON oder OFF beschreiben läßt. Typischerweise sind dies Register, die die Adressierungseinheiten beeinflussen und deren Werte AS wissen muß, um korrekte Adressierungen zu erzeugen. Wichtig ist, daß man AS mit ASSUME diese Werte nur mitteilt, es wird kein Maschinencode erzeugt, der diese Werte in die entsprechenden Register lädt!
Im Gegensatz zu seinen ,,Vorgängern'' wie 6800 und 6502 kann beim 6809 die Lage der direct page, d.h. des Adressbereiches, der mit ein Byte langen Adressen erreichbar ist, frei bestimmt werden. Dazu dient das sog. ,,Direct Page Register'' (DPR), das die Seitennummer festlegt. Ihm muß man mittels ASSUME einen passenden Wert zuweisen, wenn man einen anderen Wert als die Vorgabe von 0 in DPR schreibt, sonst werden Adressen falscher Länge erzeugt...
Um mit seinen nur 16 Bit breiten Adreßoperanden einen 1 Mbyte großen Adreßraum ansprechen zu können, bedient sich der 68HC16 einer Reihe von Bank-Registern, die die fehlenden oberen vier Adreßbits nachliefern. Davon ist das EK-Register für absolute Datenzugriffe (nicht Sprünge!) zuständig. AS überprüft bei jeder absoluten Adressierung, ob die oberen vier Bits der Adresse mit dem über ASSUME spezifizierten Wert übereinstimmen. Differieren die Werte, gibt AS eine Warnung aus. Der Vorgabewert für EK ist 0.
Im Maximum-Modus wird der erweiterte Adreßraum dieser Prozessorreihe durch eine Reihe von Bank-Registern adressiert. Diese tragen die Namen DP (Register 0..3, absolute Adressen), EP (Register 4/5) und TP (Stack). Den momentanen Wert von DP benötigt AS, um zu überprüfen, ob absolute Adressen in der momentan adressierbaren Bank liegen; die beiden anderen Register werden nur für indirekte Adressierungen benutzt und entziehen sich daher der Kontrolle; ob man ihre Werte angibt oder nicht, ist daher Geschmackssache. Wichtig ist dagegen wieder das BR-Register, das angibt, auf welchen 256-Byte-Bereich mit kurzen Adressen zugegriffen werden kann. Allen Registern ist gemeinsam, daß AS keine Initialwerte für sie annimmt, da sie nach einem Prozessor-Reset undefiniert sind; wer absolut adressieren will, muß daher auf jeden Fall DR und DP belegen!
Die Mikrokontroller dieser Reihe kennen für den JSR-Befehl eine besondere Adressierungsart ,,special page'', mit deren Hilfe man Sprünge in die oberste Seite des internen ROMs kürzer kodieren kann. Diese ist natürlich vom jeweiligen Chip abhängig, und es gibt mehr Chips, als es mit dem CPU-Befehl sinnvoll wäre, zu kodieren...also muß ASSUME herhalten, um die Lage dieser Seite vorzugeben, z.B.
ASSUME SP:$1f ,falls das interne ROM 8K groß ist.
Diese Prozessoren beinhalten eine Reihe von Registern, deren Inhalt AS kennen muß, um den korrekten Code zu erzeugen. Es handelt sich um folgende Register:
Name | Bedeutung | Wertebereich | Default |
---|---|---|---|
DT PG DPR X M |
Datenbank Code-Bank direkt adr. Seite Indexregisterbreite Akkumulatorbreite |
0-$ff 0-$ff 0-$ffff 0 oder 1 0 oder 1 |
0 0 0 0 0 |
Um mich nicht in endlose Wiederholungen zu ergehen, verweise ich für die Benutzung dieser Werte auf Kapitel 4.9. Die Handhabung erfolgt ansonsten genauso wie beim 8086, d.h. es können auch hier mehrere Werte auf einmal gesetzt werden und es wird kein Code erzeugt, der die Register mit den Werten besetzt. Dies bleibt wieder einzig und allein dem Programmierer überlassen!
Alle Prozessoren der MCS-96-Familie besitzen ab dem 80196 ein Register WSR, mit dessen Hilfe Speicherbereiche aus dem erweiterten internen RAM oder dem SFR-Bereich in Bereiche des Registerfiles eingeblendet werden und so mit kurzen Adressen angesprochen werden können. Teilt man AS mit Hilfe des ASSUME-Befehls mit, welchen Wert das WSR-Register hat, so stellt er bei absoluten Adressen automatisch fest, ob sie durch das Windowing mit 1-Byte-Adressen erreicht werden können; umgekehrt werden auch für durch das Windowing überdeckte Register automatisch lange Adressen erzeugt. Der 80296 besitzt ein zusätzliches, zweites Register WSR1, um zwei unterschiedliche Speicherbereiche gleichzeitig in das Registerfile einblenden zu können. Sollte es möglich sein, eine Speicherzelle über beide Bereiche zu adressieren, so wählt AS immer den Weg über WSR!
Der 8086 kann Daten aus allen Segmenten in einem Befehl adressieren, benötigt jedoch sog. ,,Segment-Präfixe'', wenn ein anderes Segmentregister als DS verwendet werden soll. Zusätzlich kann es sein, daß das DS-Register auf ein anderes Segment verstellt ist, um z.B. über längere Strecken nur Daten im Codesegment zu adressieren. Da AS aber keine Sinnanalyse des Codes vornimmt, muß ihm über diesen Befehl mitgeteilt werden, auf welche Segmente die Segmentregister momentan zeigen, z.B.
ASSUME CS:CODE, DS:DATA .Allen vier Segmenten des 8086 (SS,DS,CS,ES) können auf diese Weise Annahmen zugewiesen werden. Dieser Befehl erzeugt jedoch keinen Code, um die Werte auch wirklich in die Segmentregister zu laden, dies muß vom Programm getan werden.
Die Benutzung diese Befehls hat zum einen die Folge, daß AS bei sporadischen Zugriffen ins Codesegment automatisch Präfixe voranstellen kann, andererseits daß man AS mitteilen kann, daß das DS-Register verstellt wurde und man sich im folgenden explizite CS:-Anweisungen sparen kann.
Gültige Argumente hinter dem Doppelpunkt sind CODE, DATA und NOTHING. Letzterer Wert dient dazu, AS mitzuteilen, daß das Segmentregister keinen für AS verwendbaren Wert enthält. Vorinitialisiert sind folgende ASSUMEs :
CS:CODE, DS:DATA, ES:NOTHING, SS:NOTHING
Die XA-Familie besitzt einen Datenadreßraum von 16 Mbyte, ein Prozeß kann jedoch nur immer innerhalb einer 64K-Seite adressieren, die durch das DS-Register vorgegeben wird. AS muß man den momentanen Wert dieses Registers vorgeben, damit er Zugriffe auf absolute Adressen überprüfen kann.
Die Prozessoren der 29K-Familie besitzen ein Register RBP, mit dessen Hilfe Bänke von 16 Registern vor der Benutzung im User-Modus geschützt werden können. Dazu kann man ein entsprechendes Bit in diesem Register setzen. Mit ASSUME kann man AS nun mitteilen, welchen Wert RBP gerade hat. Auf diese Weise kann AS warnen, falls versucht wird, im User-Modus auf geschützte Register zuzugreifen.
Obwohl keines der Register im 80C166/167 breiter als 16 Bit ist, besitzt dieser Prozessor 18/24 Adreßleitungen, kann also bis zu 256 Kbyte/16 Mbyte adressieren. Um diesen Widerspruch unter einen Hut zu bekommen, verwendet er nicht die von Intel her bekannte (...und berüchtigte) Segmentierung oder hat unflexible Bankregister...nein, er macht Paging! Dazu wird der ,,logische'' Adreßraum von 64 Kbyte in 4 Seiten zu 16 Kbyte eingeteilt, und für jede Seite existiert ein Seitenregister (bezeichnet als DPP0...DPP3), das bestimmt, welche der physikalischen 16/1024 Seiten dort eingeblendet wird. AS versucht nun, den Adreßraum grundsätzlich mit 256 Kbyte/16 Mbyte aus der Sicht des Programmierers zu verwalten, d.h. bei absoluten Zugriffen ermittelt AS die physikalische Seite und schaut in der mit ASSUME eingestellten Seitenverteilung nach, wie die Bits 14 und 15 der logischen Adresse gesetzt werden müssen. Paßt kein Seitenregister, so wird eine Warnung ausgegeben. Defaultmäßig nimmt AS an, daß die vier Register linear die ersten 64 Kbyte abbilden, etwa in der folgenden Form:
ASSUME DPP0:0,DPP1:1,DPP2:2,DPP3:3Der 80C167 kennt noch einige Befehle, die die Seitenregister in ihrer Funktion übersteuern können. Wie diese Befehle die Adreßgenerierung beeinflussen, ist im Kapitel mit den prozessorspezifischen Hinweisen beschrieben.
Der von der Architektur her vorgegebene Datenadreßraum dieser Prozessoren (egal ob man direkt oder über das HL-Register adressiert) beträgt lediglich 256 Nibbles. Da die ,,besseren'' Familienmitglieder aber bis zu 1024 Nibbles RAM on chip haben, war Toshiba gezwungen, einen Bankingmechanismus über das DMB-Register einzuführen. AS verwaltet das Datensegment als einen durchgehenden Adreßraum und prüft bei jeder direkten Adressierung, ob die Adresse in der momentan aktiven Bank liegt. Die von AS momentan angenommene Bank kann mittels
ASSUME DMB:<0..3>festgelegt werden. Der Default ist 0.
Die Mikrokontroller der ST62-Reihe sind in der Lage, einen Teil (64
Byte) des Codebereiches in den Datenbereich einzublenden, z.B. um
Konstanten aus dem ROM zu laden. Dies bedeutet aber auch, daß
zu einem Zeitpunkt immer nur ein Teil des ROMs adressiert werden
kann. Welcher Teil dies ist, wird durch ein bestimmtes Register
bestimmt. Dem Inhalt dieses Registers kann AS zwar nicht direkt
kontrollieren, man kann ihm aber mit diesem Befehl mitteilen, wenn
man dem Register einen neuen Wert zugewiesen hat. AS kann dann
prüfen und ggfs. warnen, falls auf Adressen im Codesegment
zugegriffen wird, die nicht im ,,angekündigten'' Fenster liegt.
Hat die Variable VARI z.B. den Wert 456h, so setzt
Anstelle eines Symbols kann auch schlicht NOTHING angegeben
werden, z.B. wenn das Bank-Register temporär als Speicherzelle
benutzt wird. Dieser Wert ist auch die Voreinstellung.
Die ST9-Familie verwendet zur Adressierung von Code- und Datenbereich
exakt die gleichen Befehle. Welcher Adreßraum dabei jeweils
angesprochen wird, hängt vom Stand des DP-Flags im Flag-Register
ab. Damit AS bei absoluten Zugriffen überprüfen kann, ob
man mit Symbolen aus dem korrekten Adreßraum arbeitet (das
funktioniert natürlich nur bei absoluten Zugriffen!),
muß man ihm per ASSUME mitteilen, ob das DP-Flag
momentan auf 0 (Code) oder 1 (Daten) steht. Der Initialwert dieser
Annahme ist 0.
Diese Prozessoren besitzen ein Register (V), mit dessen Hilfe die
,,Zeropage'', d.h. die Lage der mit nur einem Byte adressierbaren
Speicherzellen sich in Seitengrenzen im Speicher frei verschieben
läßt. Da man aber aus Bequemlichkeitsgründen nicht
mit Ausdrücken wie
Da alle Instruktionsworte dieser Prozessorfamilie nur 32 Bit lang
sind, und von diesen 32 Bit nur 16 Bit für absolute Adressen
vorgesehen wurden, müssen die fehlenden oberen 8 Bit aus dem
DP-Register ergänzt werden. Bei Adressierungen kann man aber
trotzdem die volle 24-Bit-Adresse angeben, AS prüft dann, ob die
oberen 8 Bit mit dem angenommenen Inhalt von DP übereinstimmen.
Gegenüber dem LDP-Befehl weicht ASSUME darin
ab, daß man hier nicht eine beliebige Adresse aus der
Speicherbank angeben kann, das Herausziehen der oberen Bits muß
man also ,,zu Fuß'' machen, z.B. so:
Da selbst mit Hilfe von Doppelregistern (8 Bit) nicht der komplette
Adreßraum von 12 Bit zu erreichen ist, mußte NEC (wie
andere auch...) auf Banking zurückgreifen: Die oberen 4
Adreßbits werden aus dem MBS-Register geholt (welchem
demzufolge mit ASSUME Werte zwischen 0 und 15 zugeordnet
werden können), das aber nur beachtet wird, falls das
MBE-Flag auf 1 gesetzt wurde. Steht es (wie die Vorgabe ist) auf
0, so kann man die obersten und untersten 128 Nibbles des
Adreßraumes ohne Bankumschaltung erreichen. Da der 75402
weder MBE-Flag noch MBS-Register kennt, ist
für ihn der ASSUME-Befehl nicht definiert; Die
Initialwerte von MBE und MBS lassen sich daher
nicht ändern.
Gültigkeit: 29K
AMD hat die Ausnahmebehandlung für undefinierte Befehle bei der
29000-Serie so definiert, daß für jeden einzelnen Befehl
ein Exceptionvektor zur Verfügung steht. Dies legt es nahe,
durch gezielte Software-Emulationen den Befehlssatz eines kleineren
Mitgliedes dieser Familie zu erweitern. Damit nun aber AS diese
zusätzlichen Befehle nicht als Fehler anmeckert, erlaubt es
der EMULATED-Befehl, AS mitzuteilen, daß bestimmte
Befehle doch erlaubt sind. Die Prüfung, ob der momentan gesetzte
Prozessor diesen Befehl beherrscht, wird dann übergangen. Hat
man z.B. für einen Prozessor ohne Gleitkommaeinheit ein Modul
geschrieben, das aber nur mit 32-Bit-IEEE-Zahlen umgehen kann, so
schreibt man
Gültigkeit: XA
BRANCHEXT mit ON oder OFF als Argument
legt fest, ob AS kurze, nur mit einem 8-Bit-Displacement
verfügbare Sprünge automatisch ,,verlängern'' soll,
indem z.B. aus einem einfachen
Die hier beschriebenen Befehle überschneiden sich teilweise in
ihrer Funktionalität, jedoch definiert jede Prozessorfamilie
andere Namen für die gleiche Funktion. Um mit den
Standardassemblern konform zu bleiben, wurde diese Form der
Implementierung gewählt.
Sofern nicht ausdrücklich anders erwähnt, kann bei allen
Befehlen zur Datenablage (nicht bei denen zur Speicherreservierung!)
eine beliebige Zahl von Parametern angegeben werden, die der Reihe
nach abgearbeitet werden.
Gültigkeit: 680x0, M*Core, 68xx, H8, SH7000, DSP56xxx, XA,
ST7
Dieser Befehl legt eine oder mehrere Konstanten des beim durch das
Attribut bestimmten Typs im Speicher ab. Die Attribute entsprechen
den in Abschnitt 2.5 definierten,
zusätzlich ist für Byte-Konstanten die Möglichkeit
vorhanden, Stringausdrücke im Speicher abzulegen, wie z.B.
Sollte die Byte-Summe ungerade sein, so kann vom Assembler
automatisch ein weiteres Byte angefügt werden, um die
Wortausrichtung von Daten zu erhalten. Dieses Verhalten kann mit
dem PADDING-Befehl ein- und ausgeschaltet werden.
Mit diesem Befehl abgelegte Dezimalgleitkommazahlen (DC.P
...) können zwar den ganzen Bereich der extended precision
überstreichen, zu beachten ist dabei allerdings, daß die
von Motorola verfügbaren Koprozessoren 68881/68882 beim Einlesen
solcher Konstanten die Tausenderstelle des Exponenten ignorieren!
Default-Attribut ist W, also 16-Bit-Integerzahlen.
Beim DSP56xxx ist der Datentyp auf Integerzahlen festgelegt (ein
Attribut ist deshalb weder nötig noch erlaubt), die im Bereich
-8M..16M-1 liegen dürfen. Stringkonstanten sind ebenfalls
erlaubt, wobei jeweils drei Zeichen in ein Wort gepackt werden.
Gültigkeit: 680x0, M*Core, 68xx, H8, SH7x00, DSP56xxx, XA,
ST7
Mit diesem Befehl läßt sich zum einen Speicherplatz
für die angegebene Zahl im Attribut beschriebener Zahlen
reservieren. So reserviert
Die andere Bedeutung ist die Ausrichtung des Programmzählers,
die mit der Wertangabe 0 erreicht wird. So wird mit
Vorgabe für die Operandengröße ist --- wie
üblich --- W, also 16 Bit.
Beim 56xxx ist die Operandengröße auf Worte (a 24 Bit)
festgelegt, Attribute gibt es deswegen wie bei DC auch hier
nicht.
Diese Befehle stellen sozusagen das Intel-Gegenstück zu
DS und DC dar, und wie nicht anders zu erwarten ist die
Logik etwas anders:
Zum einen wird die Kennung der Operandengröße in das
Mnemonic verlegt:
ASSUME ROMBASE:VARI>>6
die AS-interne Variable auf 11h, und ein Zugriff auf VARI
erzeugt einen Zugriff auf die Adresse 56h im Datensegment.
ST9
µPD78(C)10
inrw Lo(Zaehler)
arbeiten will, übernimmt AS diese Arbeit, allerdings nur unter
der Voraussetzung, daß man ihm über einen
ASSUME-Befehl den Inhalt des V-Registers mitteilt. Wird ein
Befehl mit Kurzadressierung benutzt, so wird überprüft, ob
die obere Hälfte des Adreßausdrucks mit dem angenommenen
Inhalt übereinstimmt. Stimmt sie nicht, so erfolgt eine Warnung.
320C3x
ldp @adr
assume dp:adr>>16
.
.
.
ldi @adr,r2
75K0
EMULATED FADD,FSUB,FMUL,FDIV
EMULATED FEQ,FGE,FGT,SQRT,CLASS
bne target
automatisch eine längere Sequenz mit gleicher Funktion wird,
falls das Sprungziel zu weit von momentanen Programmzähler
entfernt ist. Für bne wäre dies z.B. die Sequenz
beq skip
jmp target
skip:
Falls für eine Anweisung aber kein passendes ,,Gegenteil''
existiert, kann die Sequenz auch länger werden, z.B.
für jbc:
jbc dobr
bra skip
dobr: jmp target
skip:
Durch dieses Feature gibt es bei Sprüngen keine eineindeutige
Zuordnung von Maschinen- und Assemblercode mehr, und bei
Vorwärtsreferenzen handelt man sich möglicherweise
zusätzliche Passes ein. Man sollte dieses Feature daher mit
Vorsicht einsetzen!
String dc.b "Hello world!\0"
Die Parameterzahl darf zwischen 1 und 20 liegen, zusätzlich darf
jedem Parameter ein in eckigen Klammern eingeschlossener
Wiederholungsfaktor vorausgehen, z.B. kann man mit
dc.b [(*+255)&$ffffff00-*]0
den Bereich bis zur nächsten Seitengrenze mit Nullen
füllen. Vorsicht! Mit dieser Funktion kann man sehr
leicht die Grenze von 1 Kbyte erzeugten Codes pro Zeile Quellcode
überschreiten!
DS.B 20
z.B. 20 Bytes Speicher,
DS.X 20
aber 240 Byte !
DS.W 0
der Programmzähler auf die nächste gerade Adresse
aufgerundet, mit
DS.D 0
dagegen auf die nächste Langwortgrenze. Eventuell dabei
freibleibende Speicherzellen sind nicht etwa mit Nullen oder NOPs
gefüllt, sondern undefiniert.
Gültigkeit: Intel, Zilog, Toshiba, NEC, TMS370, Siemens, AMD, M16(C), MELPS7700/65816, National, ST9, TMS7000, µPD77230
Zum anderen erfolgt die Unterscheidung, ob Konstantendefinition oder
Speicherreservierung, im Operanden. Eine Reservierung von Speicher
wird durch ein ? gekennzeichnet:
db ? ; reserviert ein Byte
dw ?,? ; reserviert Speicher fuer 2 Worte (=4 Byte)
dd -1 ; legt die Konstante -1 (FFFFFFFFH) ab !
Speicherreservierung und Konstantendefinition dürfen nicht in
einer Anweisung gemischt werden:
db "Hallo",? ; -->Fehlermeldung
Zusätzlich ist noch der
DUP-Operator erlaubt, der die mehrfache Ablage von
Konstantenfolgen oder die Reservierung ganzer Speicherblöcke
erlaubt:
db 3 dup (1,2) ; --> 1 2 1 2 1 2 dw 20 dup (?) ; reserviert 40 Byte Speicher.Wie man sehen kann, muß das DUP-Argument geklammert werden, darf dafür aber auch wieder aus mehreren Teilen bestehen, die selber auch wieder DUPs sein können...das ganze funktioniert also rekursiv.
DUP ist aber auch eine Stelle, an der man mit einer anderen Grenze des Assemblers in Berührung kommen kann: maximal können 1024 Byte Code oder Daten in einer Zeile erzeugt werden. Dies bezieht sich nicht auf die Reservierung von Speicher, nur auf die Definition von Konstantenfeldern!
Um mit dem M80 verträglich zu sein, darf im Z80-Modus anstelle von DB/DW auch DEFB/DEFW geschrieben werden.
Analog stellen BYTE/ADDR bzw. WORD/ADDRW beim COP8 einen Alias für DB bzw. DW dar, wobei die beiden Paare sich jedoch in der Byte-Order unterscheiden: Die Befehle, die von National zur Adreßablage vorgesehen waren, benutzen Big-Endian, BYTE bzw. WORD jedoch Little-Endian.
Der NEC 77230 nimmt mit seiner DW-Anweisung eine Sonderstellung ein: Sie funktioniert eher wie DATA bei seinen kleineren Brüdern, akzeptiert aber neben String- und Integerargumenten auch Gleitkommawerte (und legt sie prozessorspezifischen 32-Bit-Format ab). DUP gibt es nicht!
Gültigkeit: Intel, Zilog, Toshiba, NEC, TMS370, Siemens, AMD, M16(C), National, ST9, TMS7000
Dieser Befehl stellt eine Kurzschreibweise dar, um Speicherbereiche zu reservieren:
DS <Anzahl>ist eine Kurzschreibweise für
DB <Anzahl> DUP (?)dar, ließe sich also prinzipiell auch einfach über ein Makro realisieren, nur scheint dieser Befehl in den Köpfen einiger mit Motorola-CPUs groß gewordener Leute (gell, Michael?) so fest verdrahtet zu sein, daß sie ihn als eingebauten Befehl erwarten...hoffentlich sind selbige jetzt zufrieden ;-)
Gültigkeit: 6502, 68xx
Mit diesem Befehl werden im 65xx/68xx-Modus Byte-Konstanten oder ASCII-Strings abgelegt, er entspricht also DC.B beim 68000 oder DB bei Intel. Ein Wiederholungsfaktor darf analog zu DC jedem einzelnen Parameter in eckigen Klammern vorangestellt werden.
Gültigkeit: ST6, 320C2x, 320C5x, MSP, TMS9900
Dito. Ein im 320C2x/5x-Modus vor dem Befehl stehendes Label wird als untypisiert gespeichert, d.h. keinem Adreßraum zugeordnet. Der Sinn dieses Verhaltens wird bei den prozessorspezifischen Hinweisen erläutert.
Ob beim MSP bzw. TMS9900 ungerade Mengen von Bytes automatisch um ein Null-Byte ergänzt werden sollen, kann mit dem PADDING-Befehl eingestellt werden.
Gültigkeit: 6502, 68xx
Mit diesem Befehl werden im 65xx/68xx-Modus Wortkonstanten abgelegt, er entspricht also DC.W beim 68000 oder DW bei Intel. Ein Wiederholungsfaktor darf analog zu DC jedem einzelnen Parameter in eckigen Klammern vorangestellt werden.
Gültigkeit: ST6, i960, 320C2x, 320C3x, 320C5x, MSP
Für den 320C3x und i960 werden hiermit 32-Bit-Worte abgelegt, für die alle anderen Familien 16-Bit-Worte. Ein im 320C2x/5x-Modus vor dem Befehl stehendes Label wird als untypisiert gespeichert, d.h. keinem Adreßraum zugeordnet. Der Sinn dieses Verhaltens wird bei den prozessorspezifischen Hinweisen erläutert.
Gültigkeit: 320C2x, 320C5x
Hiermit werden 32-Bit-Integer im Speicher abgelegt, und zwar in der Reihenfolge LoWord-HiWord. Ein eventuell vor dem Befehl stehendes Label wird dabei wieder als untypisiert abgelegt (der Sinn dieser Maßnahme ist in den prozessorspezifischen Hinweisen erläutert).
Gültigkeit: 320C3x
Mit diesen Befehlen werden Gleitkomma-Konstanten im Speicher abgelegt, jedoch nicht im IEEE-Format, sondern in den vom Prozessor verwendeten 32- und 40-Bit-Formaten. Da 40 Bit nicht mehr in eine Speicherzelle hineinpassen, werden im Falle von EXTENDED immer derer 2 pro Wert belegt. Im ersten Wort finden sich die oberen 8 Bit (der Exponent), der Rest (Vorzeichen und Mantisse) in zweiten Wort.
Gültigkeit: 320C2x, 320C5x
Mit diesen Befehlen können 32- bzw. 64-Bit-Gleitkommazahlen im IEEE-Format im Speicher abgelegt werden. Dabei wird das niederwertigste Byte jeweils auf der ersten Speicherstelle abgelegt. Ein eventuell vor dem Befehl stehendes Label wird wieder als untypisiert gespeichert (der Sinn dieser Maßnahme ist in den prozessorspezifischen Hinweisen erläutert).
3.3.12. EFLOAT, BFLOAT, TFLOAT
Gültigkeit: 320C2x, 320C5x
Auch diese Befehle legen Gleitkommazahlen im Speicher ab, jedoch in einem nicht-IEEE-Format, das evtl. leichter von Signalprozessoren zu verarbeiten ist:
Gültigkeit: 320C2x, 320C5x
Mit diesen Befehlen können Gleitkommazahlen in einem Festkommaformat abgelegt werden. xx ist dabei eine zweistellige Zahl, mit deren Zweierpotenz der Gleitkommawert vor der Umwandlung in eine ganze Zahl multipliziert werden soll. Er bestimmt also praktisch, wieviele Bits für die Nachkommastellen reserviert werden sollen. Während aber Qxx nur ein Wort (16 Bit) ablegt, wird das Ergebnis bei LQxx in 2 Worten (LoWord zuerst) abgelegt. Das sieht dann z.B. so aus:
q05 2.5 ; --> 0050h lq20 ConstPI ; --> 43F7h 0032hMich möge niemand steinigen, wenn ich mich auf meinem HP28 verrechnet haben sollte...
Gültigkeit: PIC, 320xx, AVR, MELPS-4500, 4004, µPD772x
Mit diesem Befehl werden Daten im aktuellen Segment abgelegt, wobei sowohl Integer- als auch Stringwerte zulässig sind. Bei Strings belegt beim 16C5x/16C8x, 17C4x im Datensegment und 4500er ein Zeichen ein Wort, bei AVR, 17C4x im Codesegment, µPD772x in den Datensegmenten und 3201x/3202x passen zwei Zeichen in ein Wort (LSB zuerst), beim µPD7725 drei und beim 320C3x sogar derer 4 (MSB zuerst). Im Gegensatz dazu muß im Datensegment des 4500ers ein Zeichen auf zwei Speicherstellen verteilt werden, ebenso wie beim 4004. Der Wertebereich für Integers entspricht der Wortbreite des jeweiligen Prozessors im jeweiligen Segment. Das bedeutet, daß DATA beim 320C3x die Funktion von WORD mit einschließt (die von SINGLE übrigens auch, wenn AS das Argument als Gleitkommazahl erkennt).
Gültigkeit: PIC
Dieser Befehl legt einen durch den Parameter spezifizierte Zahl von Nullworten (=NOPs) im Speicher ab. Es können maximal 512 Nullen mit einem Befehl abgelegt werden.
Gültigkeit: COP8
Mit diesen Befehlen kann ein größerer Block von Speicher (dessen Länge in Bytes bzw. Worten der erste Parameter angibt) mit einer Byte- bzw. Wortkonstanten gefüllt werden, die durch den zweiten Parameter angegeben wird. Die Maximalgröße des Blocks beträgt 1024 Elemente für FB bzw. 512 Elemente für FW.
Gültigkeit: ST6
Mit diesen beiden Befehlen können Stringkonstanten im Speicher abgelegt werden. Während ASCII nur die reinen Daten im Speicher ablegt, versieht ASCIZ automatisch jeden angegebenen String mit einem NUL-Zeichen am Ende.
Gültigkeit: 320C2x, 320C5x
Diese Anweisungen funktionieren analog zu DATA, jedoch werden hier Integer-Ausdrücke grundsätzlich als Bytes mit einem entsprechend eingeschränkten Wertebereich betrachtet, wodurch es mögliich wird, die Zahlen zusammen mit anderen Zahlen oder Zeichen paarweise in Worte zu verpacken. Die beiden Befehle unterscheiden sich lediglich in der Reihenfolge der Bytes in einem Wort: Bei STRING wird zuerst das obere und danach das untere gefüllt, bei RSTRING ist es genau umgekehrt.
Ein eventuell vor dem Befehl stehendes Label wird wieder als untypisiert gespeichert. Der Sinn dieser Maßnahme ist im entsprechenden Kapitel mit den prozessorspezifischen Befehlen erläutert.
Gültigkeit: 6502, 68xx
Mit diesem Befehl werden im 65xx/68xx-Modus String-Konstanten abgelegt. Beachten Sie jedoch, daß im Gegensatz zum Originalassembler AS11 von Motorola (dessentwegen dieser Befehl existiert, bei AS ist diese Funktion im BYT-Befehl enthalten), String-Argumente nur in Gänsefüßchen und nicht in Hochkommas oder Schrägstrichen eingeschlossen werden dürfen! Ein Wiederholungsfaktor darf analog zu DC jedem einzelnen Parameter in eckigen Klammern vorangestellt werden.
Gültigkeit: 6502, 68xx
Dieser Befehl dient im 65xx/68xx-Modus zur Reservierung von Speicher, er entspricht DS.B beim 68000 oder DB ? bei Intel.
Gültigkeit: ST6
Dito.
3.3.22. SPACE
Gültigkeit: PIC, MELPS-4500, 3201x, 320C2x, 320C5x, AVR, µPD772x
Dieser Befehl dient zur Reservierung von Speicher. Er reserviert im Codesegment immer Wörter (10/12/14/16 Bit), im Datensegment bei den PICs Bytes, beim 4500er Nibbles sowie bei Texas ebenfalls Wörter.
Gültigkeit: 320C2x, 320C3x, 320C5x, MSP
BSS arbeitet analog zu RES, lediglich ein eventuell vor dem Befehl stehendes Symbol wird beim 320C2x/5x als untypisiert gespeichert. Der Sinn dieser Maßnahme kann im Kapitel mit den prozessorspezifischen Hinweisen nachgelesen werden.
Gültigkeit: COP8
Diese beiden Befehle stellen im COP8-Modus die zum ASMCOP von National kompatible Methode dar, Speicher zu reservieren. Während DSB nur einzelne Bytes freihält, reserviert DSW Wörter und damit effektiv doppelt soviel Bytes wie DSB.
Gültigkeit: alle Prozessoren
ALIGN mit einem Integerausdruck als Argument erlaubt es, den Programmzähler auf eine bestimmte Adresse auszurichten. Die Ausrichtung erfolgt dergestalt, daß der Programmzähler so weit erhöht wird, daß er ein ganzzahliges mehrfaches des Argumentes wird. In seiner Funktion entspricht ALIGN also DS.x 0 beim den 680x0ern, nur ist die Ausrichtung noch flexibler.
Beispiel:
align 2macht den Programmzähler gerade. Wie auch bei DS.x 0 ist der freibleibende Speicherraum undefiniert.
Gültigkeit: SH7x00
Da der SH7000-Prozessor seine Register immediate nur mit 8-Bit-Werten laden kann, AS dem Programmierer jedoch vorgaukelt, daß es eine solche Einschränkung nicht gäbe, muß er die dabei entstehenden Konstanten irgendwo im Speicher ablegen. Da es nicht sinnvoll wäre, dies einzeln zu tun (wobei jedes Mal Sprungbefehle anfallen würden...), werden die Literale gesammelt und können vom Programmierer mit diesem Befehl gezielt blockweise (z.B. am Ende eines Unterprogrammes) abgelegt werden. Zu den zu beachtenden Details und Fallen sei auf das Kapitel mit den SH7000-spezifischen Dingen hingewiesen.
Gültigkeit: alle Prozessoren
Kommen wir nun zu dem, was einen Makroassembler vom normalen Assembler unterscheidet: der Möglichkeit, Makros zu definieren (ach was ?!).
Unter Makros verstehe ich hier erst einmal eine Menge von Anweisungen (normal oder Pseudo), die mit bestimmten Befehlen zu einem Block zusammengefaßt werden und dann auf bestimmte Weise bearbeitet werden können. Zur Bearbeitung solcher Blöcke kennt der Assembler folgende Befehle:
ist der wohl wichtigste Befehl zur Makroprogrammierung. Mit der Befehlsfolge
<Name> MACRO [Parameterliste] <Befehle> ENDMwird das Makro <Name> als die eingeschlossene Befehlsfolge definiert. Diese Definition alleine erzeugt noch keinen Code! Dafür kann fortan die Befehlsfolge einfach durch den Namen abgerufen werden, das Ganze stellt also eine Schreiberleichterung dar. Um die ganze Sache etwas nützlicher zu machen, kann man der Makrodefinition eine Parameterliste mitgeben. Die Parameternamen werden wie üblich durch Kommas getrennt und müssen --- wie der Makroname selber --- den Konventionen für Symbolnamen (2.7) genügen.
Sowohl Makronamen als auch -parameter sind von einer Umschaltung von AS in den case-sensitiven Modus betroffen.
Makros sind ähnlich wie Symbole lokal, d.h. bei Definition in einer Sektion sind sie nur in dieser Sektion und ihren Untersektionen bekannt. Dieses Verhalten läßt sich aber durch die weiter unten beschriebenen Optionen PUBLIC und GLOBAL in weiten Grenzen steuern.
Neben den eigentlichen Makroparametern können in der Parameterliste auch Steuerparameter enthalten sein, die die Abarbeitung des betroffenen Makros beeinflussen; diese Parameter werden von normalen Parametern dadurch unterschieden, daß sie in geschweifte Klammern eingeschlossen sind. Es sind folgende Steuerparameter definiert:
Beim Aufruf eines Makros werden die beim Aufruf angegebenen Parameternamen überall textuell im Befehlsblock eingesetzt und der sich so ergebene Assemblercode wird normal assembliert. Sollten beim Aufruf zu wenige Parameter angegeben werden, werden Nullstrings eingefügt. Wichtig ist zu wissen, daß bei der Makroexpansion keine Rücksicht auf eventuell in der Zeile enthaltene Stringkonstanten genommen wird. Zu diesem Detail gilt die alte IBM-Regel:
It's not a bug, it's a feature!Diese Lücke kann man bewußt ausnutzen, um Parameter mittels Stringvergleichen abzuprüfen. So kann man auf folgende Weise z.B. prüfen, wie ein Makroparameter aussieht:
mul MACRO para,parb IF UpString("PARA")<>"A" MOV a,para ENDIF IF UpString("PARB")<>"B" MOV b,parb ENDIF mul ab ENDMWichtig ist bei obigem Beispiel, daß der Assembler alle Parameternamen im case-sensitiven Modus in Großbuchstaben umsetzt, in Strings aber nie eine Umwandlung in Großbuchstaben erfolgt. Die Makroparameternamen müssen in den Stringkonstanten daher groß geschrieben werden.
Für die Makroparameter gelten die gleichen Konventionen wie bei normalen Symbolen, mit der Ausnahme, daß hier nur Buchstaben und Ziffern zugelassen sind, also weder Punkte noch Unterstriche. Diese Einschränkung hat ihren Grund in einem verstecktem Feature: Der Unterstrich erlaubt es, einzelne Makroparameternamen zu einem Symbol zusammenzuketten, z.B. in folgendem Beispiel:
concat MACRO part1,part2 CALL part1_part2 ENDMDer Aufruf
concat Modul,Funktionergibt also
CALL Modul_Funktion
Um alle Klarheiten auszuräumen, ein einfaches Beispiel: Ein intelverblödeter Programmierer möchte die Befehle PUSH/POP unbedingt auch auf dem 68000 haben. Er löst das ,,Problem'' folgendermaßen:
push MACRO op MOVE op,-(sp) ENDM pop MACRO op MOVE (sp)+,op ENDMSchreibt man nun im Code
push d0 pop a2 ,so wird daraus
MOVE d0,-(sp) MOVE (sp)+,a2Eine Makrodefinition darf nicht über Includefilegrenzen hinausgehen.
In Makrorümpfen definierte Labels werden immer als lokal betrachtet, ein expliziter LOCAL-Befehl ist also nicht erforderlich (und ist auch nicht definiert). Ist es aus irgendwelchen Gründen erforderlich, so kann man es mit LABEL definieren, dessen Anwendung (wie bei BIT,SFR...) immer globale Symbole ergibt :
<Name> LABEL *Da der Assembler beim Parsing einer Zeile zuerst die Makroliste und danach die Prozessorbefehle abklappert, lassen sich auch Prozessorbefehle neu definieren. Die Definition sollte dann aber vor der ersten Benutzung des Befehles durchgeführt werden, um Phasenfehler wie im folgenden Beispiel zu vermeiden:
BSR ziel bsr MACRO target JSR ziel ENDM BSR zielIm ersten Pass ist bei der Assemblierung des BSR-Befehles das Makro noch nicht bekannt, es wird ein 4 Byte langer Befehl erzeugt. Im zweiten Pass jedoch steht die Makrodefinition sofort (aus dem ersten Pass) zur Verfügung, es wird also ein 6 Byte langer JSR kodiert. Infolgedessen sind alle darauffolgenden Labels um zwei zu niedrig, bei allen weiteren Labels sind Phasenfehler die Folge, und ein weiterer Pass ist erforderlich.
Da durch die Definition eines Makros ein gleichnamiger Maschinen- oder Pseudobefehl nicht mehr zugreifbar ist, gibt es eine Hintertür, die Originalbedeutung zu erreichen: Stellt man dem Mnemonic ein ! voran, so wird das Durchsuchen der Makroliste unterdrückt. Das kann beispielsweise nützlich sein, um Befehle in ihrer Mächtigkeit zu erweitern, z.B. die Schiebebefehle beim TLCS-90:
srl macro op,n ; Schieben um n Stellen rept n ; n einfache Befehle !srl op endm endmFortan hat der SRL-Befehl einen weiteren Parameter...
ist die eine vereinfachte Form von Makrodefinitionen für den Fall, daß eine Befehlsfolge einmal auf mehrere Operanden angewendet werden soll und danach nicht mehr gebraucht wird. IRP benötigt als ersten Parameter ein Symbol für den Operanden, und danach eine (fast) beliebige Menge von Parametern, die nacheinander in den Befehlsblock eingesetzt werden. Um eine Menge von Registern auf den Stack zu schieben, kann man z.B. schreiben
IRP op, acc,b,dpl,dph PUSH op ENDMwas in folgendem resultiert:
PUSH acc PUSH b PUSH dpl PUSH dphBenutzte Labels sind wieder für jeden Durchgang automatisch lokal.
IRPC ist eine Variante von IRP, bei der das erste Argument in den bis ENDM folgenden Zeilen nicht sukzessiv durch die weiteren Parameter, sondern durch die Zeichen eines Strings ersetzt wird. Einen String kann man z.B. also auch ganz umständlich so im Speicher ablegen:
irpc char,"Hello World" db 'CHAR' endmACHTUNG! Wie das Beispiel schon zeigt, setzt IRPC nur das Zeichen selber ein, daß daraus ein gültiger Ausdruck entsteht (also hier durch die Hochkommas, inklusive des Details, daß hier keine automatische Umwandlung in Großbuchstaben vorgenommen wird), muß man selber sicherstellen.
ist die einfachste Form der Makrobenutzung. Der im Rumpf angegebene Code wird einfach sooft assembliert, wie der Integerparameter von REPT angibt. Dieser Befehl wird häufig in kleinen Schleifen anstelle einer programmierten Schleife verwendet, um den Schleifenoverhead zu sparen.
Der Vollständigkeit halber ein Beispiel:
REPT 3 RR a ENDMrotiert den Akku um 3 Stellen nach rechts.
Symbole sind wiederum für jede einzelne Repetition lokal.
Ist das Argument von REPT kleiner oder gleich Null, so wird überhaupt keine Expansion durchgeführt. Dies ist ein Unterschied zu früheren Versionen von AS, die hier etwas ,,schlampig'' waren und immer mindestens eine Expansion ausführten.
WHILE arbeitet analog zu REPT, allerdings tritt an die Stelle einer festen Anzahl als Argument ein boolescher Ausdruck, und der zwischen WHILE und ENDM eingeschlossene Code wird sooft assenbliert, bis der Ausdruck logisch falsch wird. Im Extremfall kann dies bedeuten, daß der Code überhaupt nicht assembliert wird, falls die Bedingung bereits beim Eintritt in das Konstrukt falsch ist. Andererseits kann es natürlich auch passieren, daß die Bedingung immer wahr bleibt, und AS läuft bis an das Ende aller Tage...hier sollte man also etwas Umsicht walten lassen, d.h. im Rumpf muß eine Anweisung stehen, die die Bedingung auch beeinflußt, z.B. so:
cnt set 1 sq set cnt*cnt while sq<=1000 dc.l sq cnt set cnt+1 sq set cnt*cnt endmDieses Beispiel legt alle Quadratzahlen bis 1000 im Speicher ab.
Ein unschönes Detail bei WHILE ist im Augenblick leider noch, daß am Ende der Expansion eine zusätzliche Leerzeile, die im Quellrumpf nicht vorhanden war, eingefügt wird. Dies ist ein ,,Dreckeffekt'', der auf einer Schwäche des Makroprozessors beruht und leider nicht so einfach zu beheben ist. Hoffentlich stört es nicht allzusehr....
EXITM stellt einen Weg dar, um eine Makroexpansion oder einen der Befehle REPT, IRP oder WHILE vorzeitig abzubrechen. Eine solche Möglichkeit hilft zum Beispiel, umfangreichere Klammerungen mit IF-ENDIF-Sequenzen in Makros übersichtlicher zu gestalten. Sinnvollerweise ist ein EXITM aber selber auch immer bedingt, was zu einem wichtigen Detail führt: Der Stack, der über momentan offene IF- oder SWITCH-Konstrukte Buch führt, wird auf den Stand vor Beginn der Makroexpansion zurückgesetzt. Dies ist für bedingte EXITM's zwingend notwendig, da das den EXITM-Befehl in irgendeiner Form einschließende ENDIF oder ENDCASE nicht mehr erreicht wird und AS ohne einen solchen Trick eine Fehlermeldung erzeugen würde. Weiterhin ist es für verschachtelte Makrokonstruktionen wichtig, zu beachten, daß EXITM immer nur das momentan innerste Konstrukt abbricht! Wer aus seiner geschachtelten Konstruktion vollständig ,,ausbrechen'' will, muß auf den höheren Ebenen ebenfalls EXITM's vorsehen!
FUNCTION ist zwar kein Makrobefehl im engeren Sinne, da
hierbei aber ähnliche Mechanismen wie bei Makroersetzungen
angewendet werden, soll er hier beschrieben werden.
Dieser Befehl dient dazu, neue Funktionen zu definieren, die in
Formelausdrücken wie die vordefinierten Funktionen verwendet
werden können. Die Definition muß in folgender Form
erfolgen:
Die Argumentnamen (in diesem Falle CH) müssen den
gleichen härteren Symbolkonventionen genügen wie Parameter
bei einer Makrodefinition, d.h. die Sonderzeichen . und _ sind nicht
erlaubt.
Selbstdefinierte Funktionen werden genauso benutzt wie eingebaute,
d.h. mit einer durch Kommas getrennten, geklammerten Argumentliste:
Bei dem Aufruf der Funktion werden die Argumente nur einmal berechnet
und danach an allen Stellen der Formel eingesetzt, um den
Rechenaufwand zu reduzieren und Seiteneffekte zu vermeiden. Bei
Funktionen mit mehreren Argumenten müssen die einzelnen
Argumente bei der Benutzung durch Kommata getrennt werden.
ACHTUNG! Analog wie bei Makros kann man mit der Definition
von Funktionen bestehende Funktionen umdefinieren. Damit lassen sich
auch wieder Phasenfehler provozieren. Solche Definitionen sollten
daher auf jeden Fall vor der ersten Benutzung erfolgen!
Da die Berechnung des Funktionsergebnisses anhand des
Formelausdruckes auf textueller Ebene erfolgt, kann der Ergebnistyp
von dem Typ des Eingangsargumentes abhängen. So kann bei
folgender Funktion
Bei der Definition und Ansprache von Funktionen wird im
case-sensitiven Modus zwischen Groß- und Kleinschreibung
unterschieden, im Gegensatz zu eingebauten Funktionen!
Gültigkeit: alle Prozessoren
Der Assembler unterstützt die bedingte Assemblierung mit Hilfe
der Konstrukte IF... sowie SWITCH... . Diese
Befehle wirken zur Assemblierzeit, indem entsprechend der Bedingung
Teile übersetzt oder übersprungen werden. Diese Befehle
sind also nicht mit den IF-Statements höherer
Programmiersprachen zu vergleichen (obwohl es sehr verlockend
wäre, den Assembler um die Strukturierungsbefehle höherer
Sprachen zu erweitern...).
Die folgenden Konstrukte dürfen beliebig (bis zum
Speicherüberlauf) geschachtelt werden.
IF ist das gebräuchlichere und allgemeiner verwendbare
Konstrukt. Die allgemeine Form eines IF-Befehles lautet
folgendermaßen:
Die ELSEIF-Teile sind optional, d.h. auf IF darf
auch direkt ENDIF folgen, ein parameterloses ELSEIF
bildet aber immer den letzten Zweig. Ein ELSEIF bezieht sich
immer auf das letzte, noch nicht abgeschlossene IF.
Neben IF sind noch folgende weitere bedingte Befehle
definiert:
Anstelle von ELSEIF darf auch ELSE geschrieben
werden, weil das wohl alle so gewohnt sind....
SWITCH ist ein Spezialfall von IF und für den
Fall gedacht, daß ein Ausdruck mit einer Reihe von Werten
verglichen werden soll. Dies ist natürlich auch mit IF
und einer Reihe von ELSEIFs machbar, die folgende Form
Es ist möglich, bei den CASE-Anweisungen mehrere, durch
Kommata getrennte Werte anzugeben, um den entsprechenden Block in
mehreren Fällen assemblieren zu lassen. Der
ELSECASE-Zweig dient wiederum als ,,Auffangstelle'' für den
Fall, daß keine der CASE-Bedingungen greift. Fehlt er
und fallen alle Prüfungen negativ aus, so gibt AS eine Warnung
aus.
Auch wenn die Wertelisten der CASE-Teile sich
überlappen, so wird immer nur ein Zweig ausgeführt,
und zwar bei Mehrdeutigkeiten der erste.
SWITCH dient nur der Einleitung des ganzen Konstruktes;
zwischen ihm und dem ersten CASE darf beliebiger Code stehen
(andere IFs dürfen aber nicht offen bleiben!), im Sinne
eines durchschaubaren Codes sollte davon aber kein Gebrauch gemacht
werden.
Gültigkeit: alle Prozessoren
Mit PAGE kann man AS die Dimensionen des Papiers, auf dem
das Listing ausgedruckt werden soll, mitteilen. Als erster Parameter
wird dabei die Anzahl von Zeilen angegeben, nach der AS automatisch
einen Zeilenvorschub ausgeben soll. Zu berücksichtigen ist
allerdings, daß bei dieser Angabe die Kopfzeilen inklusive
einer evtl. mit TITLE spezifizierten Zeile nicht
mitgerechnet werden. Der Minimalwert für die Zeilenzahl ist 5,
der Maximalwert 255. Eine Angabe von 0 führt dazu, daß AS
überhaupt keine automatischen Seitenvorschübe
ausführt, sondern nur noch solche, die explizit durch
NEWPAGE-Befehle oder implizit am Ende des Listings (z.B. vor der
Symboltabelle) von AS ausgelöst wurden.
Die Angabe der Breite des Listings in Zeichen kann als optionaler
zweiter Parameter erfolgen und erfüllt zwei Zwecke: Zum einen
läuft der Zeilenzähler von AS korrekt weiter, wenn eine
Quell-Zeile über mehrere Listing-Zeilen geht, zum anderen gibt
es Drucker (wie z.B. Laserdrucker), die beim Überschreiten des
rechten Randes nicht automatisch in eine neue Zeile umbrechen,
sondern den Rest einfach ,,verschlucken''. Aus diesem Grund
führt AS auch den Zeilenumbruch selbstständig durch, d.h.
zu lange Zeilen werden in Bruchstücke zerlegt, die eine
Länge kleiner oder gleich der eingestellten Länge haben. In
Zusammenhang mit Druckern, die einen automatischen Zeilenumbruch
besitzen, kann das aber zu doppelten Zeilenvorschüben
führen, wenn man als Breite exakt die Zeilenbreite des Druckers
angibt. Die Lösung in einem solchen Fall ist, als Zeilenbreite
ein Zeichen weniger anzugeben. Die eingestellte Zeilenbreite darf
zwischen 5 und 255 Zeichen liegen; analog zur Seitenlänge
bedeutet ein Wert von 0, daß AS keine Splittung der
Listing-Zeilen vornehmen soll; eine Berücksichtigung von zu
langen Zeilen im Listing beim Seitenumbruch kann dann natürlich
auch nicht mehr erfolgen.
Die Defaulteinstellung für die Seitenlänge ist 60 Zeilen,
für die Zeilenbreite 0; letztere Wert wird auch angenommen,
wenn PAGE nur mit einem Argument aufgerufen wird.
ACHTUNG! AS hat keine Möglichkeit, zu
überprüfen, ob die eingestellte Listing-Länge und
Breite mit der Wirklichkeit übereinstimmen!
NEWPAGE kann dazu benutzt werden, einen Seitenvorschub zu
erzwingen, obwohl die Seite noch gar nicht voll ist. Dies kann z.B.
sinnvoll sein, um logisch voneinander getrennte Teile im
Assemblerprogramm auch seitenmäßig zu trennen. Der
programminterne Zeilenzähler wird zurückgesetzt, der
Seitenzähler um Eins heraufgezählt. Der optionale Parameter
steht in Zusammenhang mit einer hierarchischen Seitennumerierung, die
AS bis zu einer Kapiteltiefe von 4 unterstützt. 0 bedeutet dabei
immer die tiefste Kapitelebene, der Maximalwert kann sich
während des Laufes verändern, wenn das auch verwirrend
wirken kann, wie folgendes Beispiel zeigt:
Mit dem Befehl
Zwischen der Bedeutung von MACEXP für Makros und der
für alle anderen makroartigen Konstrukte (z.B. REPT)
besteht ein subtiler Unterschied: Während Makros intern ein Flag
besitzen, das anzeigt, ob Expansionen dieses Makros ausgegeben werden
sollen oder nicht, wirkt MACEXP direkt auf alle anderen
Konstrukte, die ,,vor Ort'' aufgelöst werden. Der Sinn dieser
Differenzierung besteht darin, daß es Makros geben kann, die
ausgetestet sind und die man nicht mehr sehen will, andere aber sehr
wohl noch. MACEXP dient hier als Default für das bei
der Definition des Makros zu setzende Flag, der mit den
Steuerparametern NOEXPAND/EXPAND übersteuert werden
kann.
Die momentane Einstellung läßt sich aus dem Symbol
MACEXP auslesen.
funktioniert wie MACEXP und akzeptiert die gleichen
Parameter, arbeitet aber wesentlich radikaler: Mit
Die momentane Einstellung läßt sich aus dem Symbol
LISTING (0=OFF, 1=ON, 2=NOSKIPPED,
3=PURECODE) auslesen.
Bei der Listingausgabe auf Druckern ist es oftmals sinnvoll, den
Drucker in eine andere Betriebsart (z.B. Schmalschrift) umzuschalten
und am Ende des Listings diese Betriebsart wieder zu deaktivieren.
Mit diesen Befehlen kann die Ausgabe dieser Steuerfolgen
automatisiert werden, indem man mit
Bei der Ausgabe dieser Strings unterscheidet der Assembler
nicht, wohin das Listing geschickt wird, d.h.
Druckersteuerzeichen werden rücksichtslos auch auf den
Bildschirm geschickt!
Beispiel :
Bei Epson-Druckern ist es sinnvoll, für die breiten Listings in
den Kompreßdruck zu schalten. Die beiden Zeilen
Normalerweise versieht der Assembler bereits jede Listingseite mit
einer Titelzeile, die Quelldatei, Datum und Uhrzeit enthält. Mit
diesem Befehl kann man den Seitenkopf um eine beliebige
zusätzliche Zeile erweitern. Der anzugebende String ist dabei
ein beliebiger Stringausdruck.
Beispiel:
Bei dem bereits oben angesprochenenen Epson-Drucker soll eine
Titelzeile im Breitdruck ausgegeben werden, wozu vorher der
Kompreßmodus abgeschaltet werden muß:
RADIX mit einem numerischen Argument zwischen 2 und 36 legt
das Default-Zahlensystem für Integer-Konstanten fest, d.h. das
Zahlensystem, das angenommen wird, wenn man nichts ausdrücklich
anderes angegeben hat. Defaultmäßig ist dies 10, und bei
der Veränderung dieses Wertes sind einige Fallstricke zu
beachten, die in Abschnitt 2.8.1
beschrieben sind.
Unabhängig von der momentanen Einstellung ist das Argument
von RADIX immer dezimal; weiterhin dürfen
keine symbolischen oder Formelausdrücke verwendet werden,
sondern nur einfache Zahlenkonstanten!
Gültigkeit: alle Prozessoren
Bei den lokalen Labels und den dazu eingeführten Sektionen
handelt es sich um eine grundlegend neue Funktion, die mit Version
1.39 eingeführt wird. Da dieser Teil sozusagen ,,1.0'' ist, ist
er sicherlich noch nicht der Weisheit letzter Schluß.
Anregungen und (konstruktive) Kritik sind daher besonders
erwünscht. Insbesondere habe ich die Verwendung von Sektionen
hier so dargestellt, wie ich sie mir vorstelle. Es kann dadurch
passiert sein, daß die Realität nicht ganz meinem Modell
im Kopf entspricht. Für den Fall von Diskrepanzen verspreche
ich, daß die Realität der Dokumentation angepaßt
wird, und nicht umgekehrt, wie es bei größeren Firmen
schon einmal vorgekommen sein soll...
AS erzeugt keinen linkfähigen Code (und wird es wohl auch nicht
in näherer Zukunft tun :-( ). Diese Tatsache zwingt
dazu, ein Programm immer im ganzen zu übersetzen. Dieser Technik
gegenüber hätte eine Aufteilung in Linker-Module einige
Vorteile:
Eine Sektion stellt einen durch spezielle Befehle eingerahmten
Teil des Assembler-Programmes dar und hat einen vom Programmierer
festlegbaren, eindeutigen Namen:
Defaultmäßig unterscheidet AS Groß-und
Kleinschreibung in Sektions- namen nicht; schaltet man jedoch in den
case-sensitiven Modus um, so wird die Schreibweise genauso wie bei
Symbolnamen berücksichtigt.
Die bisher beschriebene Aufteilung würde in etwa der Sprache C
entsprechen, in der alle Funktionen auf gleicher Ebene nebeneinander
stehen. Da mein ,,hochsprachliches'' Vorbild aber Pascal ist, bin ich
noch einen Schritt weiter gegangen:
Es ist erlaubt, in einer Sektion weitere Sektionen zu definieren,
analog zu der Möglichkeit in Pascal, in einer Prozedur/Funktion
weitere Prozeduren zu definieren. Dies zeigt folgendes Beispiel:
Diese Regel kann man durchbrechen, indem man explizit an den
Symbolnamen die Sektion anhängt, aus der man das Symbol holen
will, und zwar in eckigen Klammern am Ende des Symbolnamens:
Analog zu Pascal ist es erlaubt, daß verschiedene Sektionen
Untersektionen gleichen Namens haben dürfen, das Prinzip der
Lokalität verhindert hier Irritationen. M.E. sollte man davon
aber trotzdem sparsamen Gebrauch machen, da in Symbol-und
Querverweisliste Symbole zwar mit der Sektion, in der sie definiert
wurden, gekennzeichnet werden, aber nicht mit der über dieser
Sektion evtl. liegenden ,,Sektionshierarchie'' (das hätte
einfach den Platz in der Zeile gesprengt); Unterscheidungen sind
dadurch nicht erkennbar.
Da ein SECTION-Befehl von selber kein Label definiert,
besteht hier ein wichtiger Unterschied zu Pascal: Eine
Pascal-Prozedur kann ihre Unterprozeduren/funktionen automatisch
,,sehen'', unter AS muß man noch einen Einsprungpunkt extra
definieren. Das kann man z.B. mit folgendem Makro-Pärchen tun:
Natürlich ist mit dieser Definition das Problen noch nicht ganz
gelöst, bisher ist das Einsprung-Label ja noch lokal und von
außen nicht zu erreichen. Wer nun meint, man hätte das
Label einfach nur vor der SECTION-Anweisung plazieren müssen,
sei jetzt bitte ruhig, denn er verdirbt mir den Übergang auf das
nächste Thema:
Die PUBLIC-Anweisung erlaubt es, die Zugehörigkeit
eines Symbols zu einer bestimmten Sektion zu verändern. Es ist
möglich, mit einem PUBLIC-Befehl mehrere Symbole zu
bearbeiten, ohne Beschränkung der Allgemeinheit will ich aber
ein Beispiel mit nur einer Variable verwenden: Im einfachsten Falle
erklärt man ein Symbol als vollständig global, d.h. es ist
von allen Stellen des Programmes ansprechbar:
Angesichts des hierarchischen Sektionenkonzepts erscheint die
Methode, ein Symbol als vollständig global zu definieren,
reichlich brachial. Es geht aber auch etwas differenzierter, indem
man zusätzlich einen Sektionsnamen angibt:
Mit diesem Werkzeug kann das obige Prozedurmakro nun Sinn ergeben:
Falls mehrere Untersektionen versuchen, ein Symbol gleichen Namens in
die gleiche Obersektion zu exportieren, meckert AS über doppelt
definierte Symbole, was an sich ja korrekt ist. War das gewollt, so
muß man die Symbole in irgendeiner Weise ,,qualifizieren'',
damit sie voneinander unterschieden werden können. Dies ist mit
der GLOBAL-Anweisung möglich. Die Syntax von
GLOBAL ist der von PUBLIC identisch, das Symbol bleibt
aber lokal, anstatt einer höheren Sektion zugeordnet zu werden.
Stattdessen wird ein weiteres Symbol gleichen Werts erzeugt, dem
jedoch der Untersektionsname mit einem Unterstrich vorangestellt
wird, und nur dieses Symbol wird der Sektionsangabe entsprechend
öffentlich gemacht. Definieren z.B. zwei Sektionen A
und B ein Symbol SYM und exportieren es mit
GLOBAL zu ihrer Vatersektion, so werden dort die Symbole unter
den Namen A_SYM und B_SYM eingeordnet.
Falls zwischen Quell- und Zielsektion mehrere Stufen stehen sollten,
so wird entsprechend der komplette Namenszweig von der Ziel- bis zur
Quellsektion dem Symbolnamen vorangestellt.
So schön das bisher besprochene Modell ist, ein bei Pascal nicht
auftauchendes Detail macht Ärger: die bei Assembler
möglichen Vorwärtsreferenzen. Bei Vorwärtsreferenzen
kann es sein, daß AS im ersten Pass auf ein Symbol einer
höheren Sektion zugreift. Dies ist an sich nicht weiter
tragisch, solange im zweiten Pass das richtige Symbol genommen wird,
es können aber Unfälle der folgenden Art passieren:
Die mehrstufige Suche in der Symboltabelle und die Entscheidung, mit
welchem Attribut ein Symbol eingetragen werden soll, kosten
naturgemäß etwas Rechenzeit. Ein 1800 Zeilen langes
8086-Programm z.B. wurde nach der Umstellung auf Sektionen statt in
33 in 34,5 Sekunden assembliert (80386 SX, 16MHz, 3 Durchgänge).
Der Overhead hält sich also in Grenzen: Ob man ihn in Kauf
nehmen will, ist (wie am Anfang erwähnt) eine Frage des
Geschmacks; man kann AS genauso gut ohne Sektionen verwenden.
Gültigkeit: alle Prozessoren
Mit diesem Befehl weist man den AS an, die in der Parameterliste
angegebenen Symbole (egal ob Integer, Gleitkomma oder String) im
Sharefile mit ihren Werten abzulegen. Ob eine solche Datei
überhaupt und in welchem Format erzeugt wird, hängt von den
in 2.4 beschriebenen
Kommandozeilenschaltern ab. Findet AS diesen Befehl und es wird keine
Datei erzeugt, führt das zu einer Warnung.
VORSICHT! Ein eventuell der Befehlszeile anhängender
Kommentar wird in die erste, ausgegebene Zeile mit übertragen
(sofern die Argumentliste von SHARED leer ist, wird nur der
Kommentar ausgegeben). Falls die Share-Datei für C oder Pascal
erzeugt wird, sind einen C/Pascal-Kommentar schließende
Zeichenfolgen (*/ bzw. *)) im Kommentar zu vermeiden. AS prüft
dies nicht!
Gültigkeit: alle Prozessoren
Dieser Befehl fügt die im Parameter angegebene Datei (die
optional in Gänsefüßchen eingeschlossen sein darf) so
im Text ein, als ob sie dort stehen würde. Dieser Befehl ist
sinnvoll, um Quelldateien aufzuspalten, die alleine nicht in den
Speicher passen würden oder um sich ''Toolboxen'' zu erzeugen.
Falls der angegebene Dateiname keine Endung hat, wird er automatisch
um die Endung INC erweitert.
Mit der Kommandozeilenoption
Aus Kompatibilitätsgründen ist es erlaubt, den Namen in
Gänsefüßchen zu schreiben,
Sollte der Dateiname eine Pfadangabe enthalten, so wird die Suchliste
ignoriert.
Gültigkeit: alle Prozessoren
BINCLUDE dient dazu, in den von AS erzeugten Code
Binärdaten einzubetten, die von einem anderen Programm erzeugt
wurden (das kann natürlich theoretisch auch von AS selber
erzeugter Code sein...). BINCLUDE hat drei Formen:
Es gelten die gleichen Regeln bezüglich Suchpfaden wie bei
INCLUDE.
Gültigkeit: alle Prozessoren
Der Assembler prüft zwar die Quelltexte so streng wie
möglich und liefert diffenzierte Fehlermeldungen, je nach
Anwendung kann es aber sinnvoll sein, unter bestimmten Bedingungen
zusätzliche Fehlermeldungen auszulösen, mit denen sich
logische Fehler automatisch prüfen lassen. Der Assembler
unterscheidet drei Typen von Fehlermeldungen, die über die drei
Befehle auch dem Programmierer zugänglich sind:
Diese Anweisungen ergeben nur in Zusammenhang mit bedingter
Assemblierung Sinn. Ist für ein Programm z.B. nur ein begrenzter
Adreßraum vorhanden, so kann man den Überlauf
folgendermaßen testen:
Gültigkeit: alle Prozessoren
READ ist sozusagen das Gegenstück zu der vorigen
Befehlsgruppe: mit ihm ist es möglich, während der
Assemblierung Werte von der Tastatur einzulesen. Wozu das gut sein
soll? Um das darzulegen, soll hier ausnahmsweise einmal das Beispiel
vor die genauere Erläuterung gezogen werden:
Ein Programm benötigt zum Datentransfer einen Puffer mit einer
zur Übersetzungszeit festzulegenden Größe. Um die
Größe des Puffers festzulegen, könnte man sie einmal
mit EQU in einem Symbol ablegen, es geht aber auch
interaktiv mit READ :
READ ähnelt sehr stark dem SET- Befehl, nur
daß der dem Symbol zuzuweisende Wert nicht rechts vom
Schlüsselwort steht, sondern von der Tastatur eingelesen wird.
Dies bedeutet z.B. auch, daß AS anhand der Eingabe automatisch
festlegt, ob es sich um eine Integer- oder Gleitkommazahl oder einen
String handelt und anstelle einzelner Konstanten auch ganze
Formelausdrücke eingegeben werden können.
READ darf entweder nur einen Parameter oder zwei Parameter
haben, denn die Meldung zur Eingabeaufforderung ist optional. Fehlt
sie, so gibt AS eine aus dem Symbolnamen konstruierte Meldung aus.
Gültigkeit: alle Prozessoren
Defaultmäßig ist einer Prozessorfamilie eine bestimmte
Schreibweise von Integer-Konstanten zugeordnet (die i.a. der
Herstellervorgabe entspricht, solange der nicht eine allzu
abgefahrene Syntax benutzt...). Nun hat aber jeder seine
persönlichen Vorlieben für die eine oder andere
Schreibweise und kann gut damit leben, daß sich seine Programme
nicht mehr mit dem Standard-Assembler übersetzen lassen. Setzt
man ein
Die momentane Einstellung kann aus dem gleichnamigen Symbol
ausgelesen werden.
Gültigkeit: alle Prozessoren
END kennzeichnet das Ende des Assemblerprogrammes. Danach
noch in der Quelldatei stehende Zeilen werden ignoriert.
WICHTIG: END darf zwar aus einem Makro heraus aufgerufen
werden, der Stapel der bedingten Assemblierung wird aber nicht
automatisch abgeräumt. Das folgende Konstrukt führt daher
zu einer Fehlermeldung:
END war eigentlich schon immer in AS definiert, nur war es
bei früheren Versionen von AS aus Kompatibilität zu anderen
Assemblern vorhanden und hatte keine Wirkung.
Ich habe mich bemüht, die einzelnen Codegeneratoren
möglichst kompatibel zu den Originalassemblern zu halten, jedoch
nur soweit, wie es keinen unvertretbaren Mehraufwand bedeutete.
Wichtige Unterschiede, Details und Fallstricke habe ich im folgenden
aufgelistet.
,,Wo gibt es denn das zu kaufen, den HC11 in NMOS?'', fragt jetzt
vielleicht der eine oder andere. Gibt es natürlich nicht, aber
ein H läßt sich nun einmal nicht in einer Hexzahl
darstellen (ältere Versionen von AS hätten solche Namen
deswegen nicht akzeptiert), und dann habe ich die Buchstaben gleich
ganz weggelassen...
Sicher hat es ein bißchen den Anflug einer Schnapsidee, einen
Prozessor, der eher für den Einsatz in Workstations konzipiert
wurde, in AS einzubauen, der sich ja eher an Programmierer von
Einplatinencomputern wendet. Aber was heute noch das Heißeste
vom Heißen ist, ist es morgen schon nicht mehr, und sowohl der
Z80 als auch der 8088 haben ja inzwischen die Mutation von der
Personal Computer-CPU zum sog. ,,Mikrocontroller'' vollzogen. Mit dem
Erscheinen von MPC505 und PPC403 hat sich die Vermutung dann auch
bestätigt, daß IBM und Motorola diese Prozessorserie auf
allen Ebenen durchdrücken wollen.
Die Unterstützung ist momentan noch nicht vollständig: Als
Pseudobefehle zur Datenablage werden momentan provisorisch die
Intel-Mnemonics unterstützt und es fehlen die etwas
ungewöhnlicheren, in [43] genannten
RS6000-Befehle (die aber hoffentlich keiner vermißt...). Das
wird aber nachgeholt, sobald Informationen verfügbar sind!
Motorola, was ist nur in Dich gefahren! Wer bei Dir ist nur auf das
schmale Brett gekommen, die einzelnen parallelen Datentransfers
ausgerechnet durch Leerzeichen zu trennen! Wer immer nun seine Codes
etwas übersichtlicher formatieren will, z.B. so:
Sei's drum; Motorola hat es so definiert, und ich kann es nicht
ändern. Als Trennung der Operationen sind statt Leerzeichen auch
Tabulatoren zugelassen, und die einzelnen Teile sind ja wieder ganz
normal mit Kommas getrennt.
In [38] steht, daß bei den
Befehlen MOVEC, MOVEM, ANDI und ORI auch die
allgemeineren Mnemonics MOVE, AND und OR verwendet
werden können. Bei AS geht das (noch) nicht.
Bei der Assemblersyntax dieser Prozessoren hat Hitachi reichlich bei
Motorola abgekupfert (was so verkehrt ja nun auch nicht war...), nur
leider wollte die Firma unbedingt ihr eigenes Format für
Hexadezimalzahlen einführen, und dazu noch eines, das
ähnlich wie bei Microchip Hochkommas verwendet. Das konnte (und
wollte) ich bei AS nicht nachvollziehen, bei dem Hochkommas zur
Einrahmung von ASCII-Sequenzen benutzt werden. Anstelledessen werden
Zahlen in der üblichen Motorola-Syntax geschrieben, d.h. mit
einem Dollarzeichen.
Leider hat Hitachi auch hier wieder das Extrawurst-Format für
Hexadezimalzahlen verwendet, und wieder habe ich in AS das nicht
nachvollzogen...bitte Motorola-Syntax benutzen!
Bei der Verwendung von Literalen und dem LTORG-Befehl sind
einige Details zu beachten, wenn man nicht auf einmal mit
eigenartigen Fehlermeldungen konfrontiert werden will:
Literale existieren, weil der Prozessor nicht in der Lage ist,
Konstanten außerhalb des Bereiches von -128 bis 127 mit
immediate-Adressierung zu laden. AS (und der Hitachi-Assembler)
verstecken diese Unzulänglichkeit, indem sie automatisch
entsprechende Konstanten im Speicher ablegen, die dann mittels
PC-relativer Adressierung angesprochen werden. Die Frage, die sich
nun erhebt, ist die, wo diese Konstanten im Speicher abgelegt werden
sollen. AS legt sie nicht sofort ab, sondern sammelt sie so lange
auf, bis im Programm eine LTORG-Anweisung auftritt. Dort
werden alle Konstanten abgelegt, wobei deren Adressen mit ganz
normalen Labels versehen werden, die man auch in der Symboltabelle
sehen kann. Ein Label hat die Form
Die Durchnumerierung mit n ist erforderlich, weil ein
Literal in einer Sektion mehrfach auftreten kann. Dies ist einmal
bedingt dadurch, daß die PC-relative Adressierung nur positive
Displacements erlaubt, einmal mit LTORG abgelegte Literale
also im folgenden Code nicht mitbenutzt werden können,
andererseits auch, weil die Reichweite der Displacements
beschränkt ist (512 bzw. 1024 Byte). Ein automatisches
LTORG am Ende des Programmes oder beim Umschalten zu einer
anderen CPU erfolgt nicht; findet AS in einer solchen Situation noch
abzulegende Literale, so wird eine Fehlermeldung ausgegeben.
Da bei der PC-relativen Adressierung der zur Adressierung
herangezogene PC-Wert der Instruktionsadresse+4 entspricht, ist es
nicht möglich, ein Literal zu benutzen, welches direkt hinter
dem betroffenen Befehl abgelegt wird, also z.B. so:
Es ist nicht direkt möglich, aus der Zahl und Größe
der Literale auf den belegten Speicher zu schließen. U.u.
muß AS ein Füllwort einbauen, um einen Langwort-Wert auf
eine durch 4 teilbare Adresse auszurichten, andererseits kann er
möglicherweise Teile eines 32-bittigen Literals für
16-Bit-Literale mitbenutzten. Mehrfach auftretende Literale erzeugen
natürlich nur einen Eintrag. Solche Optimierungen werden
für Vorwärtsreferenzen allerdings ganz unterdrückt, da
AS den Wert dieser Literale noch nicht kennt.
Da Literale die PC-relative Adressierung ausnutzen, die nur beim
MOV-Befehl erlaubt sind, beschränken sich Literale
ebenfalls auf die Verwendung in MOV. Etwas trickreich ist
hier die Art und Weise, in der AS die Operandengröße
auswertet. Eine Angabe von Byte oder Wort bedeutet, daß AS
einen möglichst kurzen MOV-Befehl erzeugt, der den
angegebenen Wert in den unteren 8 oder 16 Bit erzeugt, d.h. die
oberen 24 oder 16 Bit werden als don't care behandelt. Gibt man
dagegen Langwort oder gar nichts an, so sagt dies aus, daß das
komplette 32-Bit-Register den angegebenen Wert enthalten soll. Das
hat z.B. den Effekt, daß in folgendem Beispiel
Wie man sieht, ist dieses ganze Literal-Konzept reichlich
kompliziert; einfacher ging's aber wirklich nicht. Es liegt leider in
der Natur der Sache, daß man manchmal Fehlermeldungen über
nicht gefundene Literale bekommt, die eigentlich logisch nicht
auftreten könnten, weil AS die Literale ja komplett in eigener
Regie verwaltet. Treten aber bei der Assemblierung Fehler erst im
zweiten Pass auf, so verschieben sich z.B. hinter der Fehlerstelle
liegende Labels gegenüber dem ersten Pass, weil AS für die
jetzt als fehlerhaft erkannten Befehle keinen Code mehr erzeugt. Da
aber Literalnamen u.a. aus den Werten von Symbolen erzeugt werden,
werden als Folgefehler davon eventuell andere Literalnamen
nachgefragt, als im ersten Pass abgelegt wurden und AS beschwert sich
über nicht gefundene Symbole...sollten also neben anderen
Fehlern solche Literal-Fehler auftreten, beseitigen Sie erst die
anderen Fehler, bevor Sie mich und alle Literale verfluchen...
Wer aus der Motorola-Ecke kommt und PC-relative Adressierung explizit
benutzen will (z.B. um Variablen lageunabhängig zu erreichen),
sollte wissen, daß beim Ausschreiben der Adressierung nach
Programmierhandbuch, also z.B. so:
Der Programmspeicher dieser Mikrokontroller ist in Seiten zu 128
Worten eingeteilt. Diese Einteilung existiert eigentlich nur
deswegen, weil es Sprungbefehle gibt, deren Ziel innerhalb der
gleichen Seite liegen darf, und andererseits ,,lange'' Exemplare, die
den ganzen Adreßbereich erreichen können. Die
Standard-Syntax von Mitsubishi verlangt eigentlich, daß Seite
und Offset als getrennte Argument geschrieben werden müssen. Da
das aber reichlich unpraktisch ist (ansonsten hat man als
Programmierer keine Veranlassung, sich um Seiten zu kümmern, mit
der Ausnahme von indirekten Sprüngen), erlaubt es AS auch
wahlweise, die Zieladresse linear zu schreiben, also z.B.
Da die undokumentierten Befehle des 6502 sich naturgemäß
in keinem Datenbuch finden, sollen sie an dieser Stelle kurz
aufgelistet werden. Die Verwendung erfolgt naturgemäß auf
eigene Gefahr, da es keine Gewähr gibt, daß alle
Maskenversionen alle Varianten unterstützen! Bei den
CMOS-Nachfolgern des 6502 funktionieren sie sowieso nicht mehr, da
diese die ensprechenden Bitkombinationen mit offiziellen Befehlen
belegen...
Es bedeuten:
3.4.7. FUNCTION
<Name> FUNCTION <Arg>,..,<Arg>,<Ausdruck>
Die Argumente sind die Werte, die sozusagen in die Funktion
,,hineingesteckt'' werden. In der Definition werden für die
Argumente symbolische Namen gebraucht, damit der Assembler bei der
Benutzung der Funktion weiß, an welchen Stellen die aktuellen
Werte einzusetzen sind. Dies kann man an folgendem Beispiel sehen:
isgit FUNCTION ch,(ch>='0')&&(ch<='9')
Diese Funktion überprüft, ob es sich bei dem Argument (wenn
man es als Zeichen interpretiert) um eine Ziffer im momentan
gültigen Zeichencode handelt (der momentane Zeichencode ist
mittels CHARSET veränderbar, daher die vorsichtige
Formulierung).
IF isdigit(Zeichen)
message "\{Zeichen} ist eine Ziffer"
ELSEIF
message "\{Zeichen} ist keine Ziffer"
ENDIF
double function x,x+x
das Ergebnis ein Integer, eine Gleitkommazahl oder sogar ein String
sein, je nach Typ des Arguments!
IF <Ausdruck 1>
<Block 1>
ELSEIF <Ausdruck 2>
<Block 2>
(evtl. weitere ELSEIFs)
ELSEIF
<Block n>
ENDIF
IF dient als Einleitung und wertet den ersten Ausdruck aus
und assembliert Block 1, falls der Ausdruck wahr (d.h. ungleich 0)
ist. Alle weiteren ELSEIF-Teile werden dann ignoriert. Falls
der Ausdruck aber nicht wahr ist, wird Block 1 übersprungen und
Ausdruck 2 ausgewertet. Sollte dieser nun wahr sein, wird Block 2
assembliert. Die Zahl der ELSEIF-Teile ist variabel und
ergibt eine IF-THEN-ELSE-Leiter beliebiger Länge. Der
dem letzten ELSEIF (ohne Parameter) zugeordnete Block wird
nur assembliert, falls alle vorigen Ausdrücke falsch ergaben und
bildet sozusagen einen ,,Default-Zweig''. Wichtig ist, daß von
den Blöcken immer nur einer assembliert wird, und zwar
der erste, dessen zugeordnetes IF/ELSEIF einen wahren
Ausdruck hatte.
3.5.2. SWITCH / CASE / ELSECASE / ENDCASE
SWITCH <Ausdruck>
...
CASE <Wert 1>
...
<Block 1>
...
CASE <Wert 2>
...
<Block 2>
...
(weitere CASE-Konstrukte)
...
CASE <Wert n-1>
...
<Block n-1>
...
ELSECASE
...
<Block n>
...
ENDCASE
bietet aber den Vorteil, daß der zu prüfende Ausdruck nur
einmal hingeschrieben und berechnet werden muß, er ist also
weniger fehleranfällig und etwas schneller als eine
IF-Kette, dafür natürlich auch nicht so flexibel.
Je nach momentan vorhandener Kapiteltiefe kann NEWPAGE
<Nummer> also an verschiedenen Stellen eine Erhöhung
bedeuten. Ein automatischer Seitenvorschub wegen Zeilenüberlauf
oder ein fehlender Parameter ist gleichbedeutend mit NEWPAGE
0. Am Ende des Listings wird vor Ausgabe der Symboltabelle ein
implizites NEWPAGE <bish. Maximum> durchgeführt,
um sozusagen ein neues Hauptkapitel zu beginnen.
Seite 1, Angabe NEWPAGE 0 -> Seite 2 Seite 2, Angabe NEWPAGE 1 -> Seite 2.1 Seite 2.1, Angabe NEWPAGE 1 -> Seite 3.1 Seite 3.1, Angabe NEWPAGE 0 -> Seite 3.2 Seite 3.2, Angabe NEWPAGE 2 -> Seite 4.1.1
MACEXP off
kann man erreichen, daß bei Makroexpansionen nur noch der
Makroaufruf und nicht der expandierte Text ausgegeben wird. Die ist
bei makrointensivem Code sinnvoll, um das Listing nicht ins Uferlose
wachsen zu lassen. Mit
MACEXP on
wird die vollständige Listingform wieder eingeschaltet, dies ist
auch die Default-Vorgabe.
LISTING off
wird überhaupt nichts mehr im Listing ausgegeben. Diese
Anweisung macht Sinn für erprobte Codeteile oder Includefiles,
um den Papierverbrauch nicht ins Unermeßliche zu steigern.
ACHTUNG! Wer später das Gegenstück vergißt,
bekommt auch keine Symboltabelle mehr zu sehen! Zusätzlich
zu ON und OFF akzeptiert LISTING auch
NOSKIPPED und PURECODE als Argument. Mit der
NOSKIPPED-Einstellung werden aufgrund bedingter Assemblierung
nicht assemblierte Teile nicht im Listing aufgeführt,
während PURECODE - wie der Name schon erahnen
läßt - auch die IF-Konstrukte selber nicht mehr
im Listing aufführt. Diese Einstellungen sind nützlich,
wenn man Makros, die anhand von Parametern verschiedene Aktionen
ausführen, benutzt, und im Listing nur noch die jeweils
benutzten Teile sehen möchte.
PRTINIT <String>
die Zeichenfolge angibt, die vor Listingbeginn an das
Ausgabegerät geschickt werden soll und mit
PRTEXIT <String>
analog den Deinitialisierungsstring. In beiden Fällen
muß <String> ein Stringausdruck sein. Die
Syntaxregeln für Stringkonstanten ermöglichen es, ohne
Verrenkungen Steuerzeichen in den String einzubauen.
PRTINIT "\15"
PRTEXIT "\18"
sorgen dafür, daß der Kompreßdruck ein- und nach dem
Druck wieder ausgeschaltet wird.
TITLE "\18\14Breiter Titel\15"
(Epson-Drucker schalten den Breitdruck automatisch am Zeilenende
aus.)
3.7. lokale Symbole
Insbesondere der letzte Punkt hat mich persönlich immer etwas
gestört: War ein Label-Name einmal am Anfang eines 2000 Zeilen
langen Programmes benutzt, so durfte er nirgendwo wieder verwendet
werden --- auch nicht am anderen Ende des Quelltextes, wo Routinen
mit ganz anderem Kontext standen. Ich war dadurch gezwungen,
zusammengesetzte Namen der Form
<Unterprogrammname>_<Symbolname>
zu verwenden, die dann Längen zwischen 15 und 25 Zeichen hatten
und das Programm unübersichlich machten. Das im folgenden
eingehender beschriebene Sektionen-Konzept sollte zumindest den
beiden letzten genannten Punkten abhelfen. Es ist vollständig
optional: Wollen Sie keine Sektionen verwenden, so lassen Sie es
einfach bleiben und arbeiten weiter wie unter den älteren
AS-Versionen.
3.7.1. Grunddefinition (SECTION/ENDSECTION)
...
<anderer Code>
...
SECTION <Sektionsname>
...
<Code in der Sektion>
...
ENDSECTION [Sektionsname]
...
<anderer Code>
...
Der Name für eine Sektion muß den Konventionen für
einen Symbolnamen entsprechen; da AS Sektions-und Symbolnamen in
getrennten Tabellen speichert, darf ein Name sowohl für ein
Symbol als auch eine Sektion verwendet werden. Sektionsnamen
müssen in dem Sinne eindeutig sein, daß auf einer Ebene
nicht zwei Sektionen den gleichen Namen haben dürfen (was es mit
den ,,Ebenen'' auf sich hat, erläutere ich im nächsten
Abschnitt). Das Argument zu ENDSECTION ist optional, es darf
auch weggelassen werden; Falls es weggelassen wird, zeigt AS den
Namen der Sektion an, der er das ENDSECTION zugeordnet hat.
Code in einer Sektion wird von AS genauso behandelt wie
außerhalb, lediglich mit drei entscheidenden Unterschieden:
Mit diesem Mechanismus kann man z.B. den Code in Module aufteilen,
wie man es mit einem Linker getan hätte. Eine feinere Aufteilung
wäre dagegen, alle Routinen in getrennte Sektionen zu verpacken.
Je nach Länge der Routinen können die nur intern
benötigten Symbole dann sehr kurze Namen haben.
3.7.2. Verschachtelung und Sichtbarkeitsregeln
sym EQU 0
SECTION ModulA
SECTION ProcA1
sym EQU 5
ENDSECTION ProcA1
SECTION ProcA2
sym EQU 10
ENDSECTION ProcA2
ENDSECTION ModulA
SECTION ModulB
sym EQU 15
SECTION ProcB
ENDSECTION ProcB
ENDSECTION ModulB
Bei der Suche nach einem Symbol sucht AS zuerst ein Symbol, das der
aktuellen Sektion zugeordnet ist, und geht danach die ganze ,,Liste''
der Vatersektionen durch, bis er bei den globalen Symbolen angekommen
ist. Im Beispiel sehen die Sektionen die in Tabelle 3.5 angegebenen Werte für das
Symbol sym.
Sektion
Wert
aus Sektion...
Global
0
Global
ModulA
0
Global
ProcA1
5
ProcA1
ProcA2
10
ProcA2
ModulB
15
ModulB
ProcB
15
ModulB
move.l #sym[ModulB],d0
Es dürfen dabei nur Sektionsnamen verwendet werden, die eine
Obersektion zur aktuellen Sektion darstellen. Als Sonderwert sind die
Namen PARENT0..PARENT9 erlaubt, mit denen man die n-ten
,,Vatersektionen'' relativ zur momentanen Sektion ansprechen
kann; PARENT0 entspricht also der momentanen Sektion
selber, PARENT1 der direkt übergeordneten usw.
Anstelle PARENT1 kann man auch kurz nur PARENT
schreiben. Läßt man dagegen den Platz zwischen den
Klammern komplett frei, also etwa so
move.l #sym[],d0 ,
so erreicht man das globale Symbol. ACHTUNG! Wenn man explizit
ein Symbol aus einer Sektion anspricht, so wird auch nur noch bei den
Symbolen dieser Sektion gesucht, der Sektionsbaum wird nicht mehr bis
nach oben durchgegangen!
proc MACRO name
SECTION name
name LABEL $
ENDM
endp MACRO name
ENDSECTION name
ENDM
Diese Beispiel zeigt gleichzeitig, daß die Lokalität von
Labels in Makros nicht von den Sektionen beeinflußt wird,
deshalb der Trick mit dem LABEL-Befehl.
PUBLIC <Name>
Da ein Symbol bei seiner Definition endgültig in der
Symboltabelle einsortiert wird, muß diese Anweisung vor
der Definition des Symbols erfolgen. Alle PUBLICs werden von
AS in einer Liste vermerkt und bei ihrer Definition aus dieser Liste
wieder entfernt. Bei Beendigung einer Sektion gibt AS Fehlermeldungen
für alle nicht aufgelösten ,,Vorwärtsreferenzen'' aus.
PUBLIC <Name>:<Sektion>
Damit wird das Symbol der genannten Sektion zugeordnet und damit auch
allen ihren Untersektionen zugänglich (es sei denn, diese
definieren wiederum ein Symbol gleichen Namens, das dann das
,,globalere'' übersteuert). Naturgemäß protestiert
AS, falls mehrere Untersektionen ein Symbol gleichen Namens auf die
gleiche Ebene exportieren wollen. Als Spezialwert für
<Sektion> sind die im vorigen Abschnitt genannten
PARENTx-Namen zugelassen, um das Symbol genau n Ebenen
hinaufzuexportieren. Es sind als Sektionen nur der momentanen Sektion
übergeordnete Sektionen zugelassen, also keine, die im Baum
aller Sektionen in einem anderen Zweig stehen. Sollten dabei mehrere
Sektionen den gleichen Namen haben (dies ist legal), so wird die
tiefste gewählt.
proc MACRO name
SECTION name
PUBLIC name:PARENT
name LABEL $
ENDM
Diese Einstellung entspricht dem Modell von Pascal, in der eine
Unterprozedur auch nur von ihrem ,,Vater'' gesehen werden kann,
jedoch nicht vom ,,Großvater''.
loop: .
<Code>
..
SECTION sub
.. ; ***
bra.s loop
..
loop: ..
ENDSECTION
..
jmp loop ; Hauptschleife
AS wird im ersten Pass das globale Label loop verwenden,
sofern das Programmstück bei <Code> hinreichend
lang ist, wird er sich über eine zu große Sprungdistanz
beklagen und den zweiten Pass erst gar nicht versuchen. Um die
Uneindeutigkeit zu vermeiden, kann man den Symbolnamen mit einem
expliziten Bezug versehen:
bra.s loop[sub]
Falls ein lokales Symbol häufig referenziert wird, können
die vielen Klammern mit dem FORWARD-Befehl eingespart
werden. Das Symbol wird damit explizit als lokal angekündigt. AS
wird dann bei Zugriffen auf dieses Symbol automatisch nur im lokalen
Symbolbereich suchen. In diesem Falle müßte an der mit
*** gekennzeichneten Stelle dafür der Befehl
FORWARD loop
stehen. Damit FORWARD Sinn macht, muß es nicht nur vor
der Definition des Symbols, sondern vor seiner ersten Benutzung in
der Sektion gegeben werden. Ein Symbol gleichzeitig privat und
öffentlich zu definieren, ergibt keinen Sinn und wird von AS
auch angemahnt.
3.7.5. Geschwindigkeitsaspekte
3.8.2. INCLUDE
-i <Pfadliste>
läßt sich eine Liste von Verzeichnissen angeben, in denen
automatisch zusätzlich nach der Includedatei gesucht werden
soll. Wird die Datei nicht gefunden, so ist dies ein fataler
Fehler, d.h. der Assembler bricht sofort ab.
INCLUDE stddef51
und
INCLUDE "stddef51.inc"
sind also äquivalent. ACHTUNG! Wegen dieser Wahlfreiheit
ist hier nur eine Stringkonstante, aber kein Stringausdruck
zulässig!
BINCLUDE <Datei>
In dieser Form wird die Datei komplett eingebunden.
BINCLUDE <Datei>,<Offset>
In dieser Form wird der Inhalt der Datei ab <Offset> bis zum
Ende der Datei eingebunden.
BINCLUDE <Datei>,<Offset>,<Len>
In dieser Form werden <Len> Bytes ab Offset <Offset>
eingebunden.
3.8.4. MESSAGE, WARNING, ERROR und FATAL
Allen drei Befehlen ist das Format gemeinsam, in dem die
Fehlermeldung angegeben werden muß: Ein beliebig
(berechneter?!) Stringausdruck, der damit sowohl eine Konstante als
auch variabel sein darf.
ROMSize equ 8000h ; 27256-EPROM
ProgStart: ..
<das eigentliche Programm>
..
ProgEnd:
if ProgEnd-ProgStart>ROMSize
error "\aDas Programm ist zu lang!"
endif
Neben diesen fehlererzeugenden Befehlen gibt es noch den Befehl
MESSAGE, der einfach nur eine Meldung auf der Konsole bzw. im
Listing erzeugt. Seine Benutzung ist den anderen drei Befehlen
gleich.
IF MomPass=1
READ "Puffer (Bytes)",BufferSize
ENDIF
Auf diese Weise können Programme sich während der
Übersetzung interaktiv konfigurieren, man kann sein Programm
z.B. jemandem geben, der es mit seinen Parametern übersetzen
kann, ohne im Quellcode ,,herumstochern'' zu müssen. Die im
Beispiel gezeigte IF- Abfrage sollte übrigens immer
verwendet werden, damit der Anwender nur einmal mit der Abfrage
belästigt wird.
RELAXED ON
an den Programmanfang, so kann man fortan alle Schreibweisen beliebig
gemischt und durcheinander verwenden; bei jedem Ausdruck versucht AS
automatisch zu ermitteln, welche Schreibweise verwendet wurde.
Daß diese Automatik nicht immer das Ergebnis liefert, das man
sich vorgestellt hat, ist auch der Grund, weshalb diese Option
explizit eingeschaltet werden muß (und man sich davor
hüten sollte, sie einfach in einem existierenden Programm
dazuzusetzen): Ist nicht durch vor- oder nachgestellte Zeichen zu
erkennen, daß es sich um Intel- oder Motorola-Konstanten
handelt, wird im C-Modus gearbeitet. Eventuell vorangestellte,
eigentlich überflüssige Nullen haben in diesem Modus
durchaus eine Bedeutung:
move.b #08,d0
Diese Konstante würde als Oktalkonstante verstanden werden, und
weil Oktalzahlen nur Ziffern von 0..7 enthalten können,
führt das zu einem Fehler. Dabei hätte man in diesem Fall
noch Glück gehabt, bei der Zahl 077 z.B. hätte man
ohne Meldung Probleme bekommen. Ohne RELAXED-Modus wäre
in beiden Fällen klar gewesen, daß es sich um dezimale
Konstanten handelt.
IF KeineLustMehr
END
ENDIF
Optional darf END auch einen Integer-Ausdruck als Argument
haben, der den Startpunkt des Programmes vermerkt. Dieser wird von AS
in einem speziellen Record der Datei vermerkt und kann z.B. von P2HEX
weiterverarbeitet werden.
4. Prozessorspezifische Hinweise
,,Jemand, der sagt, etwas sei unmöglich,sollte
wenigstens so kooperativ sein, denjenigen, der es gerade tut,
nicht am Arbeiten zu hindern.''
Ab und zu ist man gezwungen, seine Meinung zu revidieren. Vor einigen
Versionen hatte ich an dieser Stelle noch behauptet, ich könne
es im Parser von AS nicht realisieren, daß man die Argumente
von BSET/BCLR bzw. BRSET/BRCLR auch mit Leerzeichen
trennen kann. Offensichtlich kann selbiger aber mehr, als ich
vermutet habe...nach der soundsovielten Anfrage habe ich mich noch
einmal drangesetzt, und jetzt scheint es zu laufen. Man darf sowohl
Leerzeichen als auch Kommas verwenden, aber nicht in allen Varianten,
um es nicht uneindeutig zu machen: Es gibt zu jeder Befehlsvariante
zwei Möglichkeiten; eine, die nur Kommas verwendet, sowie eine,
wie sie von Motorola wohl definiert wurde (leider sind die
Datenbücher nicht immer so gut wie die zugehörige
Hardware...):
Bxxx abs8 #mask entspricht Bxxx abs8,#mask
Bxxx disp8,X #mask entspricht Bxxx disp8,X,#mask
BRxxx abs8 #mask adr entspricht BRxxx abs8,#mask,adr
BRxxx disp8,X #mask adr entspricht BRxxx disp8,X,#mask,adr
Dabei steht xxx entweder für SET oder
CLR und #mask für die zu verwendende Bitmaske; der
Lattenzaun ist dabei optional. Anstelle des X-Registers darf
natürlich auch Y verwendet werden.
move x:var9 ,r0
move y:var10,r3 ,
der ist gekniffen, weil das Leerzeichen als Trennung paralleler
Datentransfers erkannt wird!
LITERAL_s_xxxx_n .
Dabei repräsentiert s den Typ des Literals.
Unterschieden werden Literale, die 16-Bit-Konstanten (s=W),
32-Bit-Konstanten (s=L) oder Vorwärtsreferenzen, bei
denen AS die Operandengröße nicht im voraus erkennen kann
(s=F), enthalten. Für W oder L
bedeutet xxxx den hexadezimal geschriebenen Wert der
Konstante, bei Vorwärtsreferenzen, bei denen man den Literalwert
ja noch nicht kennt, bezeichnet xxxx eine einfache
Durchnumerierung. n kennzeichnet das wievielte Auftreten
dieses Literals in dieser Sektion. Literale machen ganz normal die
Lokalisierung durch Sektionen mit, es ist daher zwingend
erforderlich, in einer Sektion entstandene Literale mit
LTORG auch dort abzulegen!
mov #$1234,r6
ltorg
Da der Prozessor dann aber sowieso versuchen würde, Daten als
Code auszuführen, sollte diese Situation in realen Programmen
nicht auftreten. Wesentlich realer ist aber ein anderer Fallstrick:
Wird hinter einem verzögerten Sprung PC-relativ zugegriffen, so
ist der Programmzähler bereits auf die Sprungzieladresse
gesetzt, und das Displacement wird relativ zum Sprungziel+2
berechnet. Im folgenden Beispiel kann daher das Literal nicht
erreicht werden:
bra Target
mov #$12345678,r4 ; wird noch ausgefuehrt
.
.
ltorg ; hier liegt das Literal
.
.
Target: mov r4,r7 ; hier geht es weiter
Da Target+2 hinter dem Literal liegt, würde sich ein negatives
Displacement ergeben. Besonders haarig wird es, wenn mit den
Befehlen JMP, JSR, BRAF oder BSRF verzweigt wird:
Da AS die Zieladresse hier nicht ermitteln kann (sie ergibt sich erst
zur Laufzeit aus dem Registerinhalt), nimmt AS hier eine Adresse an,
die nach Möglichkeit nie paßt, so daß PC-relative
Adressierung gänzlich unmöglich wird.
mov.b #$c0,r0
mov.w #$c0,r0
mov.l #$c0,r0
der erste Befehl echte immediate-Adressierung erzeugt, der zweite und
dritte jedoch ein Wort-Literal benutzen: Da das Bit 7 in der Zahl
gesetzt ist, erzeugt der Byte-Befehl effektiv $FFFFFFC0 im Register,
was nach der Konvention nicht das wäre, was man im zweiten und
dritten Fall haben möchte. Im dritten Fall reicht auch ein
Wort-Literal, weil das gelöschte Bit 15 des Operanden vom
Prozessor in Bit 16..31 fortgesetzt wird.
mov.l @(Var,PC),r8
keine implizite Umrechnung der Adresse auf ein Displacement
erfolgt, d.h. der Operand wird so eingesetzt, wie er ist (und
würde in diesen Beispiel wohl mit hoher Wahrscheinlichkeit eine
Fehlermeldung hervorrufen...). Will man beim SH7x00 PC-relativ
adressieren, so tut man das einfach mit ,,absoluter'' Adressierung,
die auf Maschinenebene ja gar nicht existiert:
mov.l Var,r8
Hier wird das Displacement korrekt berechnet (es gelten
natürlich die gleichen Einschränkungen für das
Displacement wie bei Literalen).
bl $1234
anstelle
bl $24,$34 .
& | binäres UND |
| | binäres ODER |
^ | binäres EXOR |
<< | logischer Linksshift |
>> | logischer Rechtsshift |
<<< | Linksrotation |
>>> | Rechtsrotation |
<- | Zuweisung |
(..) | Inhalt von .. |
.. | Bits .. |
A | Akkumulator |
X,Y | Indexregister X,Y |
S | Stapelzeiger |
An | Akkumulatorbit n |
M | Operand |
C | Carry |
PCH | obere Hälfte Programmzähler |
Anweisung | : | JAM, KIL oder CRS |
Funktion | : | keine, Prozessor wird angehalten |
Adressierungsmodi | : | implizit |
Anweisung | : | SLO |
Funktion | : | M<-((M)<<1)|(A) |
Adressierungsmodi | : | absolut lang/kurz, X-indiziert lang/kurz, |
Y-indiziert lang, X/Y-indirekt | ||
Anweisung | : | ANC |
Funktion | : | A<-(A)&(M), C<- A7 |
Adressierungsmodi | : | immediate |
Anweisung | : | RLA |
Funktion | : | M<-((M)<<1)&(A) |
Adressierungsmodi | : | absolut lang/kurz, X-indiziert lang/kurz, |
Y-indiziert lang, X/Y-indirekt | ||
Anweisung | : | SRE |
Funktion | : | M<-((M)>>1)^(A) |
Adressierungsmodi | : | absolut lang/kurz, X-indiziert lang/kurz, |
Y-indiziert lang, X/Y-indirekt | ||
Anweisung | : | ASR |
Funktion | : | A<-((A)&(M))>>1 |
Adressierungsmodi | : | immediate |
Anweisung | : | RRA |
Funktion | : | M<-((M)>>>1)+(A)+(C) |
Adressierungsmodi | : | absolut lang/kurz, X-indiziert lang/kurz, |
Y-indiziert lang, X/Y-indirekt | ||
Anweisung | : | ARR |
Funktion | : | A<-((A)&(M))>>>1 |
Adressierungsmodi | : | immediate |
Anweisung | : | SAX |
Funktion | : | M<-(A)&(X) |
Adressierungsmodi | : | absolut lang/kurz, Y-indiziert kurz, |
Y-indirekt | ||
Anweisung | : | ANE |
Funktion | : | M<-((A)&$ee)|((X)&(M)) |
Adressierungsmodi | : | immediate |
Anweisung | : | SHA |
Funktion | : | M<-(A)&(X)&(PCH+1) |
Adressierungsmodi | : | X/Y-indiziert lang |
Anweisung | : | SHS |
Funktion | : | X<-(A)&(X), S<-(X), M<-(X)&(PCH+1) |
Adressierungsmodi | : | Y-indiziert lang |
Anweisung | : | SHY |
Funktion | : | M<-(Y)&(PCH+1) |
Adressierungsmodi | : | Y-indiziert lang |
Anweisung | : | SHX |
Funktion | : | M<-(X)&(PCH+1) |
Adressierungsmodi | : | X-indiziert lang |
Anweisung | : | LAX |
Funktion | : | A,X<-(M) |
Adressierungsmodi | : | absolut lang/kurz, Y-indiziert lang/kurz, |
X/Y-indirekt | ||
Anweisung | : | LXA |
Funktion | : | X04<-(X)04 & (M)04, |
A04<-(A)04 & (M)04 | ||
Adressierungsmodi | : | immediate |
Anweisung | : | LAE |
Funktion | : | X,S,A<-((S)&(M)) |
Adressierungsmodi | : | Y-indiziert lang |
Anweisung | : | DCP |
Funktion | : | M <-(M)-1, Flags<-((A)-(M)) |
Adressierungsmodi | : | absolut lang/kurz, X-indiziert lang/kurz, |
Y-indiziert lang, X/Y-indirekt | ||
Anweisung | : | SBX |
Funktion | : | X<-((X)&(A))-(M) |
Adressierungsmodi | : | immediate |
Anweisung | : | ISB |
Funktion | : | M<-(M)+1, A<-(A)-(M)-(C) |
Adressierungsmodi | : | absolut lang/kurz, X-indiziert lang/kurz, |
Y-indiziert lang, X/Y-indirekt | ||
Die Mikrokontroller dieser Reihe haben ein sehr nettes, verstecktes Feature: Setzt man mit dem Befehl SET das Bit 5 des Statusregisters, so wird bei allen arithmetischen Operationen (und Ladebefehlen) der Akkumulator durch die durch das X-Register adressierte Speicherzelle ersetzt. Dieses Feature syntaxmäßig sauber zu integrieren, ist bisher nicht geschehen, d.h. es kann bisher nur im ,,Handbetrieb'' (SET...Befehle mit Akkuadressierung...CLT) genutzt werden.
Nicht alle MELPS-740-Prozessoren implementieren alle Befehle. An dieser Stelle muß der Programmierer aufpassen, daß er nur die Befehle benutzt, die auch wirklich vorhanden sind, da AS die Prozessoren dieser Familie nicht näher unterscheidet. Die Besonderheiten der Special-Page-Adressierung werden bei der Erklärung von ASSUME näher erläutert.
Offensichtlich haben diese beiden Prozessorfamilien ausgehend vom
6502 (über ihre 8-bittigen Vorgänger) etwas disjunkte
Entwicklungswege hinter sich. Kurz aufgelistet, ergeben sich folgende
Unterschiede:
Besonders tückisch sind die Befehle PHB, PLB
und TSB: diese Befehle haben jeweils eine völlig andere
Funktion und Kodierung!
Leider tun diese Prozessoren mit ihrem Speicher etwas, was für
mich auf der nach oben offenen Perversitätsskala noch vor der
Intel-mäßigen Segmentierung rangiert: sie banken ihn!
Nunja, dies ist wohl der Preis für die
6502-Aufwärtskompatibilität; wie dem auch sei, damit AS den
gewünschten Code erzeugen kann, muß man ihn über
den ASSUME-Befehl über den Inhalt einiger Register in
Kenntnis setzen:
Das M-Flag bestimmt, ob die Akkumulatoren A und B 8 Bit (1) oder 16
Bit (0) breit sein sollen. Analog entscheidet das Flag X über
die Breite der Indexregister X und Y. AS benötigt die
Information über die Registerbreite bei unmittelbarer
Adressierung (#<Konstante>), ob das Argument 8 oder 16
Bit breit sein soll.
Der Speicher ist in 256 Bänke zu 64 Kbyte geteilt. Da alle
Register im Prozessor nur maximal 16 Bit breit sind, kommen die
obersten 8 Adreßbits aus 2 speziellen Bank-Registern: DT
liefert die oberen 8 Bits bei Datenzugriffen, PG erweitert den
16-bittigen Programmzähler auf 24 Bit. Die vom 6502 her bekannte
,,Zero-Page'' ist mittels des 16 Bit breiten Registers DPR frei
innerhalb der ersten Bank verschiebbar. Trifft AS nun im Code auf
eine Adresse (egal ob in einem absoluten, indizierten oder indirekten
Ausdruck), so versucht er der Reihe nach folgende
Adressierungsvarianten:
Die oben geschilderte, automatische Festlegung der
Adreßlänge läßt sich auch durch die Verwendung
von Präfixen übersteuern. Stellt man der Adresse ein <,
> oder >> ohne trennendes Leerzeichen voran, so wird eine
Adresse mit 1, 2 oder 3 Bytes benutzt, unabhängig davon, ob dies
die optimale Länge ist. Benutzt man eine für diesen Befehl
nicht erlaubte oder für die Adresse zu kurze Länge, gibt es
eine Fehlermeldung.
Um die Portierung von 6502-Programmen zu erleichtern, verwendet AS
für Hexadezimalkonstanten die Motorola-Syntax und nicht die von
Mitsubishi übrigens für die 740er favorisierte
Intel/IEEE-Schreibweise. Ich halte erstere auch für die bessere
Schreibweise, und die Entwickler des 65816 werden dies vermutlich
ähnlich gesehen haben (da man mittels der
RELAXED-Anweisung auch Intel-Notation benutzen kann, wird durch
diese Entscheidung auch niemand festgelegt). Ein für die
Portierung ähnlich wichtiges Detail ist, daß der
Akkumulator A als Ziel von Operationen auch weggelassen werden darf,
anstelle von LDA A,#0 darf also z.B. auch einfach LDA
#0 geschrieben werden.
Ein echtes Bonbon in dem Befehlssatz sind dagegen die
Blocktransferbefehle MVN und MVP. Etwas eigenartig
ist nur die Adreßangabe: Bit 0--15 im Indexregister, Bit 16--23
im Befehl. Bei AS gibt man als Argument für beide
Speicherblöcke einfach die vollen Adressen an, AS fischt sich
dann die passenden Bits automatisch heraus. Dies ist ein feiner, aber
wichtiger Unterschied zum Mitsubishi-Assembler, bei dem man die
oberen 8 Bit selber herausziehen muß. Richtig bequem wird es
aber erst mit einem Makro im folgendem Stil:
Sehr nett sind auch die Befehle PSH und PUL, mit
deren Hilfe es möglich ist, mit einem Befehl einen frei
wählbaren Satz von Registern auf dem Stack zu sichern oder von
ihm zu laden. Nach dem Mitsubishi-Datenbuch[27] muß die Angabe der Bitmasken
immediate erfolgen, der Programmierer soll also entweder alle
Register<->Bitstellen-Zuordnungen im Kopf behalten oder sich passende
Symbole definieren. Hier habe ich die Syntax eigenmächtig
erweitert, um die Sache etwas angenehmer zu machen: Es darf eine
Liste angegeben werden, die sowohl immediate-Ausdrücke als auch
Registernamen enthalten darf. Damit sind z.B. die Anweisungen
Nicht ganz habe ich beim Mitsubishi-Assembler die Behandlung des
PER-Befehles verstanden: Mit diesem Befehl kann man eine
16-Bit-Variable auf den Stack legen, deren Adresse relativ zum
Programmzähler angegeben wird. Es ist aus der Sicht des
Programmierers also eine absolute Adressierung einer Speicherzelle.
Nichtsdestotrotz verlangt Mitsubishi eine immediate-Adressierung, und
das Argument wird so in den Code eingesetzt, wie es im Quelltext
steht. Die Differenz muß man selber ausrechnen, was mit der
Einführung von symbolischen Assemblern ja abgeschafft werden
sollte...da ich aber auch ein bißchen ,,kompatibel'' denken
muß, enthält AS eine Kompromißlösung:
Wählt man immediate-Adressierung (also mit Gartenzaun), so
verhält sich AS wie das Original von Mitsubishi. Läßt
man ihn jedoch weg, so berechnet AS die Differenz vom Argument zum
momentanen Programmzähler und setzt diese ein.
Ähnlich sieht es beim PEI-Befehl aus, der den Inhalt
einer 16-Bit-Variablen auf der Zeropage auf den Stack legt: Obwohl
der Operand eine Adresse ist, wird wieder immediate-Adressierung
verlangt. Hier läßt AS schlicht beide Versionen zu (d.h.
mit oder ohne Gartenzaun).
Die M16-Familie ist eine Familie äußerst komplexer
CISC-Prozessoren mit einem entsprechend komplizierten Befehlssatz. Zu
den Eigenschaften dieses Befehlssatzes gehört es unter anderem,
daß bei Operationen mit zwei Operanden beide Operanden
verschiedene Längen haben dürfen. Die bei Motorola
übliche und von Mitsubishi übernommene Methode, die
Operandengröße als Attribut an den Befehl anzuhängen,
mußte daher erweitert werden: Es ist erlaubt, auch an die
Operanden selber Attribute anzuhängen. So wird im folgenden
Beispiel
Reichlich kompliziert sind auch die verketteten Adressierungsmodi;
dadurch, daß AS die Verteilung auf Kettenelemente automatisch
vornimmt, bleibt die Sache aber einigermaßen
übersichtlich. Die einzige Eingriffsmöglichkeit, die bei AS
gegeben ist (der Originalassembler von Mitsubishi/Green Hills kann da
noch etwas mehr), ist die explizite Festlegung von
Displacement-Längen mittels der Anhängsel :4,
:16 und :32.
Noch ein Stück Geschichte...leider habe ich noch keine
offizielle Dokumentation zum ersten Mikroprozessor überhaupt
gefunden, und da gibt es noch das eine oder andere Loch: Die Syntax
für Registerpaare (für 8-Bit-Operationen) ist mir noch
nicht ganz klar. Momentan ist die Syntax RRn, wobei
n ein gerader Integer im Bereich 0 bis 14 ist.
Der maximale Adreßraum dieser Prozessoren beträgt 4 Kbyte.
Dieser Raum ist jedoch nicht linear organisiert (wie könnte das
bei Intel auch anders sein...), sondern in 2 Bänke zu 2 Kbyte
geteilt. Ein Wechsel zwischen diesen beiden Bänken ist nur durch
die Befehle CALL und JMP erlaubt, indem vor dem
Sprung das höchste Adreßbit mit den Befehlen SEL
MB0 bzw. SEL MB1 vorgegeben wird. Um den Wechsel
zwischen den Bänken zu vereinfachen, ist eine Automatik in den
Befehlen JMP und CALL eingebaut, die einen dieser
beiden Befehle einfügt, falls die Adresse des Sprungbefehles und
das Sprungziel in unterschiedlichen Bänken liegen. Die explizite
Benutzung der SEL MBx-Befehle sollte daher nicht notwendig
sein (obwohl sie möglich ist) und kann die Automatik auch
durcheinanderbringen, wie in dem folgenden Beispiel:
Dem Assembler liegen die Dateien STDDEF51.INC bzw. 80C50X.INC bei, in
denen alle Bits und SFRs der Prozessoren 8051, 8052 und 80515 bzw.
80C501, 502 und 504 verzeichnet sind. Je nach Einstellung des
Prozessortyps mit dem CPU-Befehl wird dabei die korrekte
Untermenge eingebunden, die richtige Reihenfolge für den Anfang
eines Programmes ist daher
Da der 8051 keinen Befehl kennt, um die Register 0..7 auf den Stack
zu legen, muß mit deren absoluten Adressen gearbeitet werden.
Diese hängen aber von der momentan aktiven Registerbank ab. Um
diesem Mißstand etwas abzuhelfen, ist in den Include-Dateien
das Makro USING definiert, dem als Parameter die Symbole
Bank0..Bank3 gegeben werden können. Das Makro belegt
daraufhin die Symbole AR0..AR7 mit den passenden absoluten
Adressen der Register. Dieses Makro sollte nach jeder Bankumschaltung
benutzt werden. Es erzeugt selber keinen Code zur Umschaltung!
Das Makro führt in der Variablen RegUsage gleichzeitig
Buch über alle jemals benutzten Registerbänke; Bit 0
entspricht Bank 0, Bit 1 der Bank 1 usw. . Der Inhalt kann am Ende
der Quelldatei z.B. mit folgendem Codestück ausgegeben werden:
Intel hat sich beim 80C251 ja bemüht, den Übergang für
den Programmierer auf die neue Familie so weich wie möglich zu
gestalten, was darin gipfelt, daß alte Anwendungen ohne
Neuübersetzung auf dem neuen Prozessor ablaufen können.
Sobald man jedoch den erweiterten Befehlssatz der 80C251 nutzen will,
gilt es, einige Details zu beachten, die sich als versteckte
Fußangeln auftun.
An vorderster Stelle steht dabei die Tatsache, daß der 80C251
keinen getrennten Bitadreßraum mehr hat. Es sind nunmehr alle
SFRs unabhängig von ihrer Adreßlage sowie die ersten 128
Speicherstellen des internen RAMs bitadressierbar. Möglich wird
dies dadurch, daß die Bitadressierung nicht mehr über
einen zusätzlichen virtuellen Adreßraum, der andere
Adreßräume überdeckt, erfolgt, sondern so wie bei
anderen Prozessoren auch durch eine zweidimensionale Adressierung,
die aus der Speicherstelle, die das Bit beinhaltet sowie der
Bitstelle im Byte besteht. Dies bedeutet zum einen, daß bei
einer Bitangabe wie z.B. PSW.7 AS die Zerlegung der Teile links und
rechts vom Punkt selber vornimmt. Es ist also nicht mehr nötig,
mittels eines SFRB-Befehls wie noch beim 8051 explizit 8
Bitsymbole zu erzeugen. Dies bedeutet zum anderen, daß es
den SFRB-Befehl überhaupt nicht mehr gibt. Wird er in
zu portierenden 8051-Programmen benutzt, kann er durch einen
einfachen SFR-Befehl ersetzt werden.
Weiterhin hat Intel in den unterschiedlichen Adreßräumen
des 8051 gehörig aufgeräumt: Der Bereich des internen RAMs
(DATA bzw. IDATA), der XDATA-Bereich und
er bisherige CODE-Bereich wurden in einem einzigen, 16 Mbyte
großen CODE-Bereich vereinigt. Das interne RAM beginnt
bei Adresse 0, das interne ROM beginnt bei Adresse ff0000h, dorthin
muß also auch der Code mittels ORG hinverlagert
werden. Ausgelagert wurden dagegen die SFRs in einen eigenen
Adreßraum (der bei AS als IO-Segment definiert ist).
In diesem neuen Adreßraum haben sie aber die gleichen Adressen
wie beim 8051. Der SFR-Befehl kennt diesen Unterschied und
legt mit ihm erzeugte Symbole je nach Zielprozessor automatisch
ins DATA- bzw. IO-Segment. Da es keinen
Bit-Adreßraum mehr gibt, funktioniert der BIT-Befehl
völlig anders: anstelle einer linearen Adresse von 0 bis 255
beinhalten Bit-Symbole jetzt in Bit 0..7 die Adresse, in Bit 24..26
die Bitstelle. Damit ist es jetzt leider nicht mehr so einfach
möglich, Felder von Flags mit symbolischen Namen anzulegen: Wo
man beim 8051 noch z.B.
Intel möchte es gerne, daß man absolute Adressen in der
Form XX:YYYY schreibt, wobei XX eine 64K-Bank im
Adreßraum angibt bzw. mit einem S Adressen im IO-Raum
kennzeichnet. Wie man sich schon denken kann, halte ich davon nicht
allzu viel, weshalb man an allen Stellen Adressen genauso gut linear
angeben kann; lediglich um das S für die Kennzeichnung von
I/O-Adressen kommt man nicht herum, z.B. hier:
Wie auch schon beim 8051 gibt es die generischen Befehle JMP
und CALL, die je nach Adreßlage automatisch die
kürzeste Variante einsetzen. Während JMP aber die
Variante mit 24 Bit mitberücksichtigt, tut CALL dies
aus gutem Grund nicht: Der ECALL-Befehl legt nämlich im
Gegensatz zu ACALL und LCALL 3 Bytes auf den Stack,
und man hätte sonst einen CALL-Befehl, bei dem man
nicht mehr genau weiß, was er tut. Bei JMP tritt diese
Problem nicht auf.
Aus einer Sache bin ich nicht ganz schlau geworden: Der 80251 kann
auch immediate-Operanden auf den Stack legen, und zwar sowohl
einzelne Bytes als auch ganze Wörter. Für beide Varianten
ist aber der gleiche Befehl PUSH vorgesehen -- und woher
soll bitte ein Assembler bei einer Anweisung wie
Noch ein gutgemeinter Ratschlag: Wer den erweiterten Befehlssatz des
80C251 nutzt, sollte den Prozessor auch tunlichst im Source-Modus
betreiben, sonst werden alle neuen Anweisungen ein Byte länger!
Um die originären 8051-Anweisungem, die dafür im
Source-Modus länger werden, ist es nicht besonders schade: Sie
werden entweder von AS automatisch durch neue, leistungsfähigere
ersetzt oder sind be- treffen veraltete Adressierungsarten (indirekte
Adressierung mit 8-Bit-Registern).
Eigentlich hatte ich mir geschworen, die Segmentseuche der 8086er aus
diesem Assembler herauszuhalten. Da aber nun eine Nachfrage kam und
Studenten flexiblere Menschen als die Entwickler dieses Prozessors
sind, findet sich ab sofort auch eine rudimentäre
Unterstützung dieser Prozessoren in AS. Unter
,,rudimentär'' verstehe ich dabei nicht, daß der
Befehlssatz nicht vollständig abgedeckt wird, sondern daß
ich nicht den ganzen Wust an Pseudoanweisungen integriert habe, die
sich bei MASM, TASM & Co. finden. AS ist auch nicht in erster
Linie geschrieben worden, um PC-Programme zu entwickeln (Gott
bewahre, das hieße wirklich, das Rad neu zu erfinden), sondern
zur Programmentwicklung für Einplatinenrechner, die eben unter
anderem auch mit 8086ern bestückt sein können.
Für Unentwegte, die mit AS doch DOS-Programme schreiben wollen,
eine kleine Liste dessen, was zu beachten ist:
Ein weiteres großes Problem dieser Prozessoren ist deren
Assemblersyntax, deren genaue Bedeutung nur aus dem Zusammenhang
erkennbar ist. So kann im folgenden Beispiel je nach Symboltyp sowohl
unmittelbare als auch absolute Adressierung gemeint sein:
4.9. MELPS-7700/65816
Identische Funktion, jedoch andere Namen haben folgende Befehle:
65816
MELPS-7700
65816
MELPS-7700
REP
TCS
TCD
PHB
WAI CLP
TAS
TAD
PHT
WIT PHK
TSC
TDC
PLB
PHG
TSA
TDA
PLT
Aus dieser Aufzählung folgt, daß das Wissen über die
momentanen Werte von DT,PG und DPR für die Funktion von AS
essentiell ist; sind die Angaben fehlerhaft, adressiert das Programm
,,in die Wüste''. Diese Aufzählung geht übrigens davon
aus, daß alle drei Adreßlängen verfügbar sind;
sollte dies einmal nicht der Fall sein, so wird die
Entscheidungskette entsprechen kürzer.
mvpos macro src,dest,len
if MomCPU=$7700
lda #len
elseif
lda #(len-1)
endif
ldx #(src&$ffff)
ldy #(dest&$ffff)
mvp dest,src
endm
Vorsicht, Falle: Steht im Akkumulator die Zahl n, so transferiert der
Mitsubishi n Bytes, der 65816 jedoch n+1 Bytes!
psh #$0f
und
psh a,b,#$0c
und
psh a,b,x,y
äquivalent. Da die immediate-Version weiterhin erlaubt ist,
bleibt AS hier ,,aufwärtskompatibel'' zu den
Mitsubishi-Assemblern.
mov r0.b,r6.w
Register 0 8-bittig gelesen, auf 32 Bit vorzeichenerweitert und das
Ergebnis in Register 6 kopiert. Da man in 9 von 10 Fällen aber
von diesen Möglichkeiten doch keinen Gebrauch macht, kann man
weiterhin die Operandengröße an den Befehl selber
schreiben, z.B. so:
mov.w r0,r6
Beide Varianten dürfen auch gemischt verwendet werden, eine
Größenangabe am Operanden übersteuert dann den
,,Default'' am Befehl. Eine Ausnahme stellen Befehle mit zwei
Operanden dar. Bei diesen ist der Default für den Quelloperanden
die Größe des Zieloperanden. In folgendem Beispiel
mov.h r0,r6.w
wird also auf Register 0 32-bittig zugegriffen, die
Größenangabe am Befehl wird überhaupt nicht mehr
benutzt. Finden sich überhaupt keine Angaben zur
Operandengröße, so wird Wort(w) verwendet. Merke: im
Gegensatz zu den 68000ern bedeutet dies 32 und nicht 16 Bit!
000: SEL MB1
JMP 200h
AS nimmt an, daß das MB-Flag auf 0 steht und fügt
keinen SEL MB0-Befehl vor dem Sprung ein, mit der Folge,
daß der Prozessor zur Adresse A00h springt. Weiterhin ist zu
beachten, daß ein Sprungbefehl durch diesen Mechanismus unter
Umständen ein Byte länger wird.
CPU <Prozessortyp>
INCLUDE stddef51.inc ,
sonst führen die MCS-51-Pseudobefehle in der Include-Datei zu
Fehlermeldungen.
irp BANK,Bank0,Bank1,Bank2,Bank3
if (RegUsage&(2^BANK))<>0
message "Bank \{BANK} benutzt"
endif
endm
Mit der Mehrpass-Fähigkeit ab Version 1.38 wurde es
möglich, zusätzlich die Befehle JMP und
CALL einzuführen. Bei der Kodierung von Sprüngen mit
diesen Befehlen wählt AS je nach Adreßlage automatisch die
optimale Variante, d.h. SJMP/AJMP/LJMP für JMP
und ACALL/LCALL für CALL. Es ist
natürlich weiterhin möglich, die Varianten direkt zu
verwenden, um eine bestimmte Kodierung zu erzwingen.
segment bitdata
bit1 db ?
bit2 db ?
oder
defbit macro name
name bit cnt
cnt set cnt+1
endm
schreiben konnte, hilft jetzt nur noch die zweite Variante weiter,
z.B. so:
adr set 20h ; Startadresse Flags im internen RAM
bpos set 0
defbit macro name
name bit adr.bpos
bpos set bpos+1
if bpos=8
bpos set 0
adr set adr+1
endif
endm
Ein weiteres, kleines Detail: Da Intel als Kennzeichnung für den
Carry nun CY statt C bevorzugt, sollte man ein eventuell benutztes
Symbol umbenennen. AS versteht aber auch weiterhin die alte Variante
in den Befehlen CLR, CPL, SETB, MOV, ANL, und ORL.
Gleiches gilt sinngemäß für die dazugekommenen
Register R8..R15, WR0..WR30, DR0..DR28, DR56, DR60, DPX
und SPX.
Carry bit s:0d0h.7
Ohne den Präfix würde AS die absolute Adresse in das
Code-Segment legen, und dort sind ja nur die ersten 128 Byte
bitadressierbar...
push #10
wissen, ob ein Byte oder ein Wort mit dem Wert 10 auf den Stack
gelegt werden soll? Daher gilt im Augenblick die Regelung,
daß PUSH grundsätzlich ein Byte ablegt; wer ein
Wort ablegen will, schreibt einfach PUSHW anstelle
PUSH.
Allgemein unterstützt AS für diese Prozessoren nur ein
Small-Programmiermodell, d.h. ein Codesegment mit maximal 64
KByte und ein ebenfalls höchstens 64 KByte großes
Datensegment mit (für COM-Dateien uninitialisierten) Daten.
Zwischen diesen beiden Segmenten kann mit dem SEGMENT-Befehl
hin-und hergeschaltet werden. Aus dieser Tatsache folgert, daß
Sprünge immer intrasegmentär sind, sofern sie sich auf
Adressen im Codesegment beziehen. Falls weite Sprünge doch
einmal erforderlich sein sollten, können sie mit CALLF
und JMPF und einer Speicheradresse oder einen
Segment:Offset-Wert als Argument erreicht werden.
mov ax,wert
Bei AS ist immer unmittelbare Adressierung gemeint, wenn um den
Operanden keine eckigen Klammern stehen. Soll z.B. die Adresse oder
der Inhalt einer Variablen geladen werden, so ergeben sich die in
Tabelle 4.1 aufgelisteten Unterschiede.
Assembler | Adresse | Inhalt |
---|---|---|
MASM AS |
mov ax,offset vari lea ax,vari lea ax,[vari] mov ax,vari lea ax,[vari] |
mov ax,vari mov ax,[vari] mov ax,[vari] |
Der Assembler prüft bei Symbolen, ob sie im Datensegment liegen und versucht, automatisch einen passenden Segmentpräfix einzufügen, z.B. falls ohne CS-Präfix auf Symbole im Code zugegriffen wird. Dieser Mechanismus kann jedoch nur funktionieren, falls der ASSUME-Befehl (siehe dort) korrekt angewendet wurde.
Die Intel-Syntax verlangt eine Abspeicherung, ob an einem Symbol Bytes oder Wörter abgelegt wurden. AS nimmt diese Typisierung nur vor, falls in der gleichen Zeile wie das Label ein DB oder DW steht. Für alle anderen Fälle muß mit den Operatoren WORD PTR, BYTE PTR usw. explizit angegeben werden, um was für eine Operandengröße es sich handelt. Solange ein Register an der Operation beteiligt ist, kann auf diese Kennzeichnung verzichtet werden, da durch den Registernamen die Operandengröße eindeutig bestimmt ist.
Der Koprozessor in 8086-Systemen wird üblicherweise durch den TEST-Eingang des Prozessors synchronisiert, indem selbiger mit dem BUSY-Ausgang des Koprozessors verbunden wird. AS unterstützt dieses Handshaking, indem vor jedem 8087-Befehl automatisch ein WAIT-Befehl eingefügt wird. Ist dies aus irgendwelchen Gründen unerwünscht (z.B. während der Initialisierung), so muß im Opcode hinter dem F ein N eingefügt werden; aus
FINIT FSTSW [vari]wird so z.B.
FNINIT FNSTSW [vari]Diese Variante ist bei allen Koprozessorbefehlen erlaubt.
Die Prozessoren dieser Reihe sind auf eine einfache Manipulation von
Bitgruppen auf Peripherieadressen optimiert worden. Um mit solchen
Bitgruppen auch symbolisch umgehen zu können, existieren die
Befehle LIV und RIV, mit denen einer solchen
Bitgruppe ein symbolischer Name zugewiesen wird. Diese Befehle
arbeiten ähnlich wie EQU, benötigen aber drei
Parameter:
Im Maschinencode drücken sich Länge und Position durch ein
3-Bit-Feld im Instruktionswort sowie ein passende Registernummer
(LIVx bzw. RIVx) aus. Bei der Verwendung eines
symbolischen Objektes wird AS diese Felder automatisch richtig
besetzen, es ist aber auch erlaubt, die Länge als dritten
Operanden explizit anzugeben, wenn man nicht mit symbolischen
Busobjekten arbeitet. Trifft AS auf eine Längenangabe trotz
eines symbolischen Operanden, so vergleicht er beide Längen und
gibt eine Fehlermeldung bei Ungleichheit aus (das gleiche passiert
übrigens auch, wenn man bei einem MOVE-Befehl zwei
symbolische Operanden mit unterschiedlicher Länge benutzt - die
Instruktion hat einfach nur ein Längenfeld...).
Neben den eigentlichen Maschinenbefehlen des 8X30x implementiert AS
noch ähnlich wie das ,,Vorbild'' MCCAP einige
Pseudoinstruktionen, die als eingebaute Makros ausgeführt sind:
Ähnlich wie sein Vorgänger MCS/51, jedoch im Unterschied zu
seinem ,,Konkurrenten'' MCS/251 besitzt der Philips XA einen
getrennten Bitadreßraum, d.h. alle mit Bitbefehlen
manipulierbaren Bits haben eine bestimmte, eindimensionale Adresse,
die in den Maschinenbefehlen auch so abgelegt wird. Die naheliegende
Möglichkeit, diesen dritten Adreßraum (neben Code und
Daten) auch so in AS anzubieten, habe ich nicht nutzen können,
und zwar aus dem Grund, daß ein Teil der Bitadressen im
Gegensatz zum MCS/51 nicht mehr eindeutig ist: Bits mit den Adressen
256 bis 511 bezeichnen Bits der Speicherzellen 20h..3fh aus dem
aktuellen Datensegment. Dies bedeutet aber, daß diese Adressen
je nach Situation unterschiedliche Bits ansprechen können - ein
definieren von Bits mit Hilfe von DC-Befehlen, was durch ein
extra Segment möglich geworden wäre, würde also nicht
übermäßig viel Sinn ergeben. Zur Definition
einzelner, symbolisch ansprechbarer Bits steht aber nach wie vor
der BIT-Befehl zur Verfügung, mit dem beliebige
Bitadressen (Register, RAM, SFR) definiert werden können.
Für Bitadressen im internen RAM wird auch die 64K-Bank-Adresse
gespeichert, so daß AS Zugriffe überprüfen kann,
sofern das DS-Register korrekt mit ASSUME vorbesetzt wurde.
Nichts drehen kann man dagegen an den Bemühungen von AS,
potentielle Sprungziele (also Zeilen im Code mit Label) auf gerade
Adressen auszurichten. Dies macht AS genauso wie andere XA-Assembler
auch durch Einfügen von NOPs vor dem fraglichen Befehl.
Im Gegensatz zum AVR-Assembler verwendet AS defaultmäßig
das Intel-Format zur Darstellung von Hexadezimalkonstanten und nicht
die C-Syntax. OK, nicht vorher in den (freien) AVR-Assembler
hineingeschaut, aber als ich mit dem AVR-Teil anfing, gab es zum AVR
noch nicht wesentlich mehr als ein vorläufiges Datenbuch mit
Prozessortypen, die dann doch nie kamen...mit einem RELAXED
ON schafft man dieses Problem aus der Welt.
Optional kann AS für die AVRs (es geht auch für andere
CPU's, nur macht es dort keinen Sinn...) sogenannte
,,Objekt-Dateien'' erzeugen. Das sind Dateien, die sowohl Code als
auch Quellzeileninformationen enthalten und z.B. eine schrittweise
Abarbeitung auf Quellcodeebene mit dem von Atmel gelieferten
Simulator WAVRSIM erlauben. Leider scheint dieser mit
Quelldateispezifikationen, die länger als ca. 20 Zeichen sind,
seine liebe Not zu haben: Namen werden abgeschnitten oder um wirre
Sonderzeichen ergänzt, wenn die Maximallänge
überschritten wird. AS speichert deshalb in den Objekt-Dateien
Dateinamen ohne Pfadangabe, so daß es eventuell Probleme geben
könnte, wenn Dateien (z.B. Includes) nicht im Arbeitsverzeichnis
liegen.
Da es von Zilog naturgemäß keine Syntaxvorgaben für
die undokumentierten Befehle gibt und wohl auch nicht jeder den
kompletten Satz kennt, ist es vielleicht sinnvoll, diese Befehle hier
kurz aufzuzählen:
Wie auch beim Z380 ist es möglich, die Byte-Hälften von IX
und IY einzeln anzusprechen. Im einzelnen sind dies folgende
Varianten:
Die Kodierung von Schiebebefehlen besitzt noch eine undefinierte
Bitkombination, die als SLIA-Befehl zugänglich ist.
SLIA funktioniert wie SLA, es wird jedoch eine Eins und
nicht eine Null in Bit 0 eingeschoben. Dieser Befehl kann, wie alle
anderen Schiebebefehle auch, noch in einer weiteren Variante
geschrieben werden:
Da dieser Prozessor als Enkel des wohl immer noch beliebtesten
8-Bit-Prozessors konzipiert wurde, war es bei der Entwicklung
unabdingbar, daß dieser bestehende Z80-Programme ohne
Änderung ausführen kann (natürlich geringfügig
schneller, etwa um den Faktor 10...). Die erweiterten
Fähigkeiten können daher nach einem Reset mit zwei Flags
zugeschaltet werden, die XM (eXtended Mode, d.h. 32- statt
16-Bit-Adreßraum) und LW (long word mode, d.h. 32- statt 16-
Bit-Operanden) heißen. Deren Stand muß man AS über
die Befehle EXTMODE und LWORDMODE mitteilen, damit
Adressen und Konstantenwerte gegen die korrekten Obergrenzen
geprüft werden. Die Umschaltung zwischen 32- und 16-Bit-Befehlen
bewirkt natürlich nur bei solchen Befehlen etwas, die auch in
einer 32-Bit-Version existieren; beim Z380 sind das momentan leider
nur Lade- und Speicherbefehle, die ganze Aritmetik kann nur 16-bittig
ausgeführt werden. Hier sollte Zilog wohl noch einmal etwas
nachbessern, sonst kann man den Z380 selbst beim besten Willen nur
als ,,16-Bit-Prozessor mit 32-Bit-Erweiterungen'' bezeichnen...
Kompliziert wird die Sache dadurch, daß die mit LW eingestellte
Operandengröße für einzelne Befehle mit den
Präfixen DDIR W und DDIR LW übersteuert
werden kann. AS merkt sich das Auftreten solcher Befehle und schaltet
dann für den nächsten Prozessorbefehl automatisch mit um.
Andere DDIR-Varianten als W und LW sollte
man übrigens nie explizit verwenden, da AS bei zu langen
Operanden diese automatisch einsetzt, und das könnte zu
Verwirrungen führen. Die Automatik geht übrigens so weit,
daß in der Befehlsfolge
Diese Prozessoren können in zwei Betriebsarten laufen, einmal
im Minimum-Modus, der weitgehende Z80- und
TLCS-90-Quellcodekompatibilität bietet, und zum anderen im
Maximum-Modus, in dem der Prozessor erst seine wahren
Qualitäten entfaltet. Die Hauptunterschiede zwischen den beiden
Betriebsarten sind:
Je nach Betriebsart müssen demzufolge auch die 16- oder
32-Bit-Versionen der Bankregister zur Adressierung verwendet werden,
d.h. WA, BC, DE und HL im Minimum-Modus sowie XWA, XBC, XDE und XHL
im Maximum-Modus. Die Register XIX..XIZ und XSP sind immer 32
Bit breit und müssen zur Adressierung auch immer in dieser Form
verwendet werden; hier muß bestehender Z80-Code also auf jeden
Fall angepaßt werden (neben der Tatsache, daß es gar
keinen I/O-Adreßraum mehr gibt und alle I/O-Register
memory-mapped sind...).
Die von Toshiba gewählte Syntax für Registernamen ist in
der Hinsicht etwas unglücklich, als daß zur Anwahl der
vorherigen Registerbank ein Hochkomma (') benutzt wird. Dieses
Zeichen wird von den prozessorunabhängigen Teilen von AS bereits
zur Kennzeichnung von Zeichenkonstanten benutzt. Im Befehl
AS ist bei vielen Befehlen in der Syntaxprüfung weniger streng
als TAS900, bei einigen weicht er (sehr) geringfügig ab. Diese
Erweiterungen bzw. Änderungen dienen teilweise der leichteren
Portierung von bestehendem Z80-Code, teilweise einer
Schreiberleichterung und teilweise einer besseren Orthogonalität
der Assemblersyntax:
Der Makroprozessor wird TAS900 als externes Programm vorgeschaltet
und besteht aus zwei Komponenten: einem C-artigen Präprozessor
und einer speziellen Makrosprache (MPL), die an höhere
Programmiersprachen erinnert. Der Makroprozessor von AS dagegen
orientiert sich an ,,klassischen'' Makroassemblern wie dem M80 oder
MASM (beides Programme von Microsoft). Er ist fester Bestandteil des
Programmes.
TAS900 erzeugt relokatiblen Code, so daß sich mehrere, getrennt
assemblierte Teile zu einem Programm zusammenbinden lassen. AS
hingegen erzeugt direkt absoluten Maschinencode, der nicht linkbar
ist. An eine Erweiterung ist (vorläufig) nicht gedacht.
Bedingt durch den fehlenden Linker fehlen in AS eine ganze Reihe von
für relokatiblen Code erforderlichen Pseudoanweisungen, die
TAS900 implementiert. In gleicher Weise wie bei TAS900 sind folgende
Anweisungen vorhanden:
Von Toshiba existieren zwei Versionen des Prozessorkerns, wobei die
L-Variante eine ,,Sparversion'' darstellt. Zwischen TLCS-900 und
TLCS-900L macht AS folgende Unterschiede:
Vielleicht fragt sich der eine oder andere, ob bei mir die
Reihenfolge durcheinandergekommen ist, es gab ja von Toshiba zuerst
den 90er als ,,aufgebohrten Z80'' und danach den 900er als
16-Bit-Version. Nun, ich bin einfach über den 900er zum 90er
gekommen (Danke, Oliver!). Die beiden Familien sind sich sehr
artverwandt, nicht nur was ihre Syntax angeht, sondern auch ihre
Architektur. Die Hinweise für den 90er sind daher eine
Untermenge derer für den 900er: Da Schieben, Inkrementieren und
Dekrementieren hier nur um eins möglich sind, braucht und darf
diese Eins auch nicht als erstes Argument hingeschrieben werden. Bei
den Befehlen LDA, JP und CALL möchte Toshiba
wieder die Klammern um Speicheroperanden weglassen, bei AS
müssen sie aber aus Gründen der Orthogonalität gesetzt
werden (der tiefere Grund ist natürlich, daß ich mir damit
eine Sonderabfrage im Parser gespart habe, aber das sagt man nicht so
laut).
Die TLCS-90er besitzen bereits prinzipiell einen Adreßraum von
1 Mbyte, dieser Raum erschließt sich aber nur bei
Datenzugriffen über die Indexregister. AS verzichtet daher auf
eine Berücksichtigung der Bankregister und begrenzt den
Adreßraum für Code auf 64 Kbyte. Da der Bereich jenseits
aber sowieso nur über indirekte Adressierung erreichbar ist,
sollte dies keine allzu große Einschränkung darstellen.
Schon wieder Toshiba...diese Firma ist im Augenblick wirklich sehr
produktiv! Speziell dieser Sproß der Familie (Toshibas
Mikrokontroller sind sich ja alle in Binärkodierung und
Programmiermodell recht ähnlich) scheint auf den 8051-Markt
abzuzielen: Die Methode, Bitstellen durch einen Punkt getrennt an den
Adreßausdruck anzuhängen, hatte ja beim 8051 ihren
Ursprung, führt jetzt aber auch genau zu den Problemen, die ich
beim 8051 geahnt hatte: Der Punkt ist jetzt einerseits legales
Zeichen in Symbolnamen, andererseits aber auch Teil der
Adreßsyntax, d.h. AS muß Adresse und Bitstelle trennen
und einzeln weiterverarbeiten. Diesen Interessenkonflikt habe ich
vorerst so gelöst, daß der Ausdruck von hinten an
nach Punkten durchsucht wird und so der letzte Punkt als Trenner
gilt, eventuelle weitere Punkte werden dem Symbolnamen zugerechnet.
Es gilt weiterhin die flehentliche Bitte, im eigenen Interesse auf
Punkte in Symbolnamen zu verzichten, sie führen nur zu
Verwirrungen:
Mit dieser 4-Bit-Prozessorfamilie dürfte wohl das unter Ende
dessen erreicht sein, was AS unterstützen kann. Neben dem
ASSUME-Befehl für das Datenbankregister (siehe dort) ist
eigentlich nur ein Detail erwähnenswert: im Daten- und
I/O-Segment werden keine Bytes, sondern Nibbles reserviert (eben
4-Bitter...). Die Sache funktioniert ähnlich wie das
Bitdatensegment beim 8051, wo ein DB ja nur einzelne Bit
reserviert, nur daß es hier eben Nibbles sind.
Toshiba hat für diese Prozessorfamilie einen ,,erweiterten
Befehlssatz'' in Makroform definiert, um das Arbeiten mit diesem doch
recht beschränkten Befehlssatz zu erleichtern. Im Fall von AS
ist er in der Datei STDDEF47.INC definiert. Einige Befehle, deren
makromäßige Realisierung nicht möglich war, sind
allerdings ,,eingebaut'' und stehen daher auch ohne die Include-Datei
zur Verfügung:
Hier ist es zum ersten Mal passiert, daß ich einen Prozessor in
AS implementiert habe, der zu diesem Zeitpunkt noch gar nicht auf dem
Markt war. Toshiba hat sich leider auch vorläufig dazu
entschieden, diesen Prozessor ,,auf Eis'' zu legen, bis auf weiteres
wird es also auch kein Silizium geben. Das hat natürlich zur
Folge, daß dieser Teil
4.16. 8X30x
ACHTUNG! Der 8X30x unterstützt keine Bitgruppen, die
über mehrere Speicherstellen hinausreichen, so daß je nach
Startposition der Wertebereich für die Länge
eingeschränkt sein kann. AS nimmt hier keine Prüfung
vor, man bekommt lediglich zur Laufzeit merkwürdige Ergebnisse!
Die bei MCCAP ebenfalls noch vorhandenen CALL- und
RTN-Instruktionen sind mangels ausreichender Dokumentation
momentan nicht implementiert. Das gleiche gilt für einen Satz an
Pseudoinstruktionen zur Datenablage. Kommt Zeit, kommt Rat...
INC Rx LD R,Rx LD Rx,n
DEC Rx LD Rx,R LD Rx,Ry
ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,Rx
Dabei stehen Rx bzw. Ry für IXL, IXU,
IYL oder IYU. Zu beachten ist jedoch, daß in
der LD Rx,Ry-Variante beide Register aus dem gleichen
Indexregister stammen müssen.
SLIA R,(XY+d)
Dabei steht R für ein beliebiges 8-Bit-Register (aber
nicht eine Indexregisterhälfte...), und (XY+d) für
eine normale indexregister-relative Adressierung. Das Ergebnis dieser
Operation ist, daß das Schiebeergebnis zusätzlich ins
Register geladen wird. Dies funktioniert auch bei den RES-
und SET-Befehlen:
SET/RES R,n,(XY+d)
Des weiteren gibt es noch zwei versteckte I/O-Befehle:
IN (C) bzw. TSTI
OUT (C),0
Deren Funktionsweise sollte klar sein. ACHTUNG! Es gibt keine
Garantie dafür, daß alle Z80-Masken alle diese Befehle
beherrschen, und die Z80-Nachfolger lösen zuverlässig Traps
aus. Anwendung daher auf eigene Gefahr...
DDIR LW
LD BC,12345678h
automatisch der erforderliche IW-Präfix mit in die
vorangehende Anweisung hineingezogen wird, effektiv wird also der
Code
DDIR LW,IW
LD BC,12345678h
erzeugt. Der im ersten Schritt erzeugte Code für DDIR
LW wird verworfen, was an einem R im Listing zu
erkennen ist.
4.21. TLCS-900(L)
Damit AS gegen die richtigen Grenzen prüfen kann, muß man
ihm zu Anfang mit dem Befehl MAXMODE (siehe dort) mitteilen,
in welcher Betriebsart der Code ausgeführt werden wird;
Voreinstellung ist der Minimum-Modus.
ld wa',wa
erkennt AS z.B. nicht das Komma zur Parametertrennung. Dieses Problem
kann man aber umgehen, indem man ein umgekehrtes Hochkomma (`)
verwendet, z.B.
ld wa`,wa
Toshiba liefert für die TLCS-900-Reihe selber einen Assembler
(TAS900), der sich in einigen Punkten von AS unterscheidet:
Symbolkonventionen
Syntax
Makroprozessor
Ausgabeformat
Pseudoanweisungen
EQU, DB, DW, ORG, ALIGN, END, TITLE, SAVE, RESTORE,
wobei die beiden letzteren einen erweiterten Funktionsumfang haben.
Einige weitere TAS900-Pseudobefehle lassen sich durch
äquivalente AS-Befehle ersetzen (siehe Tabelle 4.2).
Die Befehle SUPMODE und MAXMODE werden nicht
beeinflußt, ebenso nicht deren initiale Einstellung OFF. Die
Tatsache, daß die L-Version im Maximum-Modus startet und keinen
Normal-Modus kennt, muß also vom Programmierer
berücksichtigt werden. AS zeigt sich jedoch insofern kulant
gegenüber der L-Variante, als daß Warnungen wegen
privilegierter Anweisungen im L-Modus unterdrückt werden.
TAS900
AS
Bedeutung/Funktion
DL <Daten>
DD <Daten>
Speicher in Langworten belegen
DSB <Zahl>
DB <Zahl> DUP (?)
Speicher byteweise reservieren
DSW <Zahl>
DW <Zahl> DUP (?)
Speicher wortweise reservieren
DSD <Zahl>
DD <Zahl> DUP (?)
Speicher langwortweise reservieren
$MIN[IMUM]
MAXMODE OFF
folgender Code im Minimum-Modus
$MAX[IMUM]
MAXMODE ON
folgender Code im Maximum-Modus
$SYS[TEM]
SUPMODE ON
folgender Code im System-Modus
$NOR[MAL]
SUPMODE OFF
folgender Code im User-Modus
$NOLIST
LISTING OFF
Assemblerlisting ausschalten
$LIST
LISTING ON
Assemblerlisting einschalten
$EJECT
NEWPAGE
neue Seite im Listing beginnen
LD CF,A.7 ; Akku Bit 7 nach Carry
LD C,A.7 ; Konstante A.7 nach Register C
Fehler in diesem Teil sind also durchaus noch möglich (und
werden natürlich bereinigt, wenn es denn einmal gehen sollte!).
Zumindest die Handvoll Beispiele in [88]
werden aber richtig übersetzt.
Wie schon beim ASSUME-Befehl beschrieben, kann AS mit der Kenntnis über den Inhalt des RBP-Registers feststellen, ob im User-Modus auf gesperrte Register zugegriffen wird. Diese Fähigkeit beschränkt sich natürlich auf direkte Zugriffe (also nicht, wenn die Register IPA...IPC benutzt werden), und sie hat noch einen weiteren Haken: da lokale Register (also solche mit Nummern>127) relativ zum Stackpointer adressiert werden, die Bits in RBP sich aber immer auf absolute Nummern beziehen, wird die Prüfung für lokale Register NICHT durchgeführt. Eine Erweiterung auf lokale Register würde bedingen, daß AS zu jedem Zeitpunkt den absoluten Wert von SP kennt, und das würde spätestens bei rekursiven Unterprogrammen scheitern...
Wie in der Erklärung des ASSUME-Befehls schon erläutert, versucht AS, dem Programmierer die Tatsache, daß der Prozessor mehr physikalischen als logischen Speicher hat, soweit als möglich zu verbergen. Beachten Sie aber, daß die DPP-Register nur Datenzugriffe betreffen und auch dort nur absolute Adressierung, also weder indirekte noch indizierte Zugriffe, da AS ja nicht wissen kann, wie die berechnete Adresse zur Laufzeit aussehen wird...Bei Codezugriffen arbeitet die Paging-Einheit leider nicht, man muß also explizit mit langen oder kurzen CALLs, JMPs oder RETs arbeiten. Zumindest bei den ,,universellen'' Befehlen CALL und JMP wählt AS automatisch die kürzeste Form, aber spätestens beim RET sollte man wissen, woher der Aufruf kam. Prinzipiell verlangen JMPS und CALLS dabei, daß man Segment und Adresse getrennt angibt, AS ist jedoch so geschrieben, daß er eine Adresse selber zerlegen kann, z.B.
jmps 12345hanstelle von
jmps 1,2345hLeider sind nicht alle Effekte der chipinternen Instruktions-Pipeline versteckt: Werden CP (Registerbankadresse), SP (Stack) oder eines der Paging-Register verändert, so steht der neue Wert noch nicht für den nächsten Befehl zur Verfügung. AS versucht, solche Situationen zu erkennen und gibt im Falle eines Falles eine Warnung aus. Aber auch diese Mimik greift nur bei direkten Zugriffen.
Mit BIT definierte Bits werden intern in einem 13-Bit-Wort abgelegt, wobei die Bitadresse in Bit 4..11 liegt und die Bitnummer in den unteren vier Bits. Diese Anordnung erlaubt es, das nächsthöhere bzw. nächstniedrigere Bit durch Inkrementieren bzw. Dekrementieren anzusprechen. Bei expliziten Bitangaben mit Punkt funktioniert das aber nicht über Wortgrenzen hinaus. So erzeugt folgender Ausdruck eine Wertebereichsüberschreitung:
bclr r5.15+1Hier muß ein BIT her:
msb bit r5.15 . . . bclr msb+1Für den 80C167/165/163 ist der SFR-Bereich verdoppelt worden; daß ein Bit im zweiten Teil liegt, wird durch ein gesetztes Bit 12 vermerkt. Leider hatte Siemens bei der Definition des 80C166 nicht vorausgesehen, daß 256 SFRs (davon 128 bitadressierbar) für Nachfolgechips nicht reichen würden. So wäre es unmöglich, den zweiten SFR-Bereich von F000H..F1DFH mit kurzen Adressen oder Bitbefehlen zu erreichen, hätten die Entwickler nicht einen Umschaltbefehl eingebaut:
EXTR #nDieser Befehl bewirkt, daß für die nächsten n Befehle (0<n<5) anstelle des normalen der erweiterte SFR-Bereich angesprochen werden kann. AS erzeugt bei diesm Befehl nicht nur den passenden Code, sondern setzt intern ein Flag, daß für die nächsten n Befehle nur Zugriffe auf den erweiterten SFR-Bereich zuläßt. Da dürfen natürlich keine Sprünge dabei sein... Bits aus beiden Bereichen lassen sich natürlich jederzeit definieren, ebenso sind komplette Register aus beiden SFR-Bereichen jederzeit mit absoluter Adressierung erreichbar. Nur die kurze bzw. Bitadressierung geht immer nur abwechselnd, Zuwiderhandlungen werden mit einer Fehlermeldung geahndet.
Ähnlich sieht es mit den Präfixen für absolute bzw. indirekte Adressierung aus: Da aber sowohl Argument des Präfixes als auch der Adreßausdruck nicht immer zur Übersetzungszeit bestimmbar sind, sind die Prüfungsmöglichkeiten durch AS sehr eingeschränkt, weshalb er es auch bei Warnungen beläßt...im einzelnen sieht das folgendermaßen aus:
extp #7,#1 ; Bereich von 112K..128K mov r0,1cdefh ; ergibt Adresse 0defh im Code mov r0,1cdefh ; -->Warnung exts #1,#1 ; Bereich von 64K..128K mov r0,1cdefh ; ergibt Adresse 0cdefh im Code mov r0,1cdefh ; -->Warnung
Ähnlich wie die MCS-48-Familie teilen auch die PICs ihren Programmspeicher in mehrere Bänke auf, da im Opcode nicht genügend Platz für die vollständige Adresse war. AS verwendet für die Befehle CALL und GOTO die gleiche Automatik, d.h. setzt die PA-Bits im Statuswort entsprechend Start- und Zieladresse. Im Gegensatz zu den 48ern ist dieses Verfahren hier aber noch deutlich problematischer:
COMF, DECF, DECFSZ, INCF, INCFSZ, RLF, RRF und SWAPFDie anderen Befehle betrachten W defaultmäßig als Akkumulator, zu dem ein Register verknüpft wird:
ADDWF, ANDWF, IORWF, MOVF, SUBWF und XORWF
Die von Microchip vorgegebene Schreibweise für Literale ist ziemlich abstrus und erinnert an die auf IBM 360/370-Systemen übliche Schreibweise (Grüße aus Neandertal...). Um nicht noch einen Zweig in den Parser einfügen zu müssen, sind bei AS Konstanten in Motorola-Syntax zu schreiben (wahlweise auch Intel oder C im RELAXED-Modus).
Dem Assembler liegt die Include-Datei STDDEF16.INC bei, in der die Adressen der Hardware-Register und Statusbits verewigt sind. Daneben enthält sie eine Liste von ,,Befehlen'', die der Microchip-Assembler als Makro implementiert. Bei der Benutzung dieser Befehlsmakros ist große Vorsicht angebracht, da sie mehrere Worte lang sind und sich somit nicht überspringen lassen!!
Für diese Prozessoren gelten im wesentlichen die gleichen Hinweise wie für ihre kleinen Brüder, mit zwei Ausnahmen: Die zugehörige Include-Datei enthält nur Registerdefinitionen, und die Probleme bei Sprungbefehlen sind deutlich kleiner. Aus der Reihe fällt nur LCALL, der einen 16-Bit-Sprung erlaubt. Dieser wird mit folgendem ,,Makro'' übersetzt:
MOVLW <Adr15..8> MOWF 3 LCALL <Adr0..7>
Diese Prozessoren können das Code-ROM seitenweise in den Datenbereich einblenden. Weil ich nicht die ganze Mimik des ASSUME-Befehles hier wiederkäuen möchte, verweise ich auf das entsprechende Kapitel (3.2.13), in dem steht, wie man mit diesem Befehl einigermaßen unfallfrei Konstanten aus dem ROM lesen kann.
Bei nähererer Betrachtung des Befehlssatzes fallen einige eingebaute ,,Makros'' auf. Die Befehle, die mir aufgefallen sind (es gibt aber vielleicht noch mehr...), sind in Tabelle 4.3 aufgelistet.
Befehl | in Wirklichkeit |
---|---|
CLR A SLA A CLR adr NOP |
SUB A,A ADD A,A LDI adr,0 JRZ PC+1 |
Insbesondere der letztere Fall verblüfft doch etwas... Leider fehlen aber einige Anweisungen wirklich. So gibt es z.B. zwar einen AND-Befehl, aber kein OR...von XOR gar nicht zu reden. In der Datei STDDEF62.INC finden sich deshalb neben den Adressen der SFRs noch einige Makros zur Abhilfe.
Der Original-Assembler AST6 von SGS-Thomson verwendet teilweise andere Pseudobefehle als AS. Außer der Tatsache, daß AS Pseudobefehle nicht mit einem vorangestellten Punkt kennzeichnet, sind folgende Befehle identisch:
ASCII, ASCIZ, BLOCK, BYTE, END, ENDM, EQU, ERROR, MACRO, ORG, TITLE, WARNINGTabelle 4.4 zeigt die AST6-Befehle, zu denen analoge in AS existieren.
AST6 | AS | Bedeutung/Funktion |
---|---|---|
.DISPLAY | MESSAGE | Meldung ausgeben |
.EJECT | NEWPAGE | neue Seite im Listing |
.ELSE | ELSEIF | bed. Assemblierung |
.ENDC | ENDIF | bed. Assemblierung |
.IFC | IF... | bed. Assemblierung |
.INPUT | INCLUDE | Include-Datei einbinden |
.LIST | LISTING, MACEXP | Listing-Einstellung |
.PL | PAGE | Seitenlänge Listing |
.ROMSIZE | CPU | Zielprozessor einstellen |
.VERS | VERSION (Symbol) | Version abfragen |
.SET | EVAL | Variablen neu setzen |
In [63] ist der '.w'-Postfix für 16-Bit-Adressen nur für speicherindirekte Operanden definiert, um zu vermerken, daß auf einer Zeropageadresse eine 16-bittige Adresse liegt; AS unterstützt ihn jedoch zusätzlich auch für absolute Adressen oder Displacements in indizierter Adressierung, um trotz eines nur 8 Bit langen Wertes (0..255) ein 16-bittiges Displacement zu erzeugen.
Die Bitadressierungsmöglichkeiten des ST9 sind relativ eingeschränkt: Mit Ausnahme des BTSET-Befehls ist es nur möglich, auf Bits innerhalb des aktuellen Arbeitsregistersatzes zuzugreifen. Eine Bit-Adresse sieht also folgendermaßen aus:
rn.[!]bwobei ! eine optionale Invertierung eines Quelloperanden bedeutet. Wird ein Bit symbolisch mittels des BIT-Befehles definiert, so wird die Registernummer im Symbolwert in Bit 7..4, die Bitnummer in Bit 3..1 und eine optionale Invertierung in Bit 0 vermerkt. AS unterscheidet direkte und symbolische Bitangaben am Fehlen eines Punktes, der Name eines Bitsymboles darf also keinen Punkt enthalten, obwohl sie an sich zulässig wären. Es ist auch zulässig, bei der Referenzierung von Bitsymbolen diese zu nachträglich zu invertieren:
bit2 bit r5.3 . . bld r0.0,!bit2Auf diese Weise ist es auch möglich, eine inverse Definition nachträglich wieder aufzuheben.
Bitdefinitionen finden sich in großer Zahl in der Include-Datei REGST9.INC, in der die Register- und Bitnamen aller On-Chip-Peripherie beschrieben sind. Beachten Sie jedoch, daß deren Nutzung nur möglich ist, wenn die Arbeitsregisterbank vorher auch auf diese Register ausgerichtet wurde!
Im Gegensatz zu der zum AST9 von SGS-Thomson gehörenden Definitionsdatei sind für AS die Namen der Peripherieregister nur als allgemeine Registernamen definiert (R...), nicht auch noch als Arbeitsregister (r...). Dies ist so, weil AS Geschwindigkeitsgründen keine Aliasnamen für Register definieren kann.
Eigentlich habe ich diesen Prozessor ja nur eingebaut, um mich über das seltsame Gebaren von SGS-Thomson zu beklagen: Als ich das 6804-Datenbuch zum ersten Mal in die Hand bekam, fühlte ich mich ob des etwas ,,unvollständigen'' Befehlssatzes und der eingebauten Makros spontan an die ST62-Serie vom gleichen Hersteller erinnert. Ein genauerer Vergleich der Opcodes förderte erstaunliches zu Tage: Ein 6804-Opcode ergibt sich durch Spiegelung aller Bits im entsprechenden ST62-OpCode! Thomson hat hier also offensichtlich etwas Prozessorkern-Recycling betrieben...wogegen ja auch nichts einzuwenden wäre, wenn nicht so eine Verschleierungstaktik betrieben werden würde: andere Peripherie, Motorola- anstelle Zilog-Syntax sowie das häßliche Detail, in Opcodes enthaltene Argumente (z.B. Bitfelder mit Displacements) nicht zu drehen. Letzterer Punkt hat mich auch nach längerem Überlegen dazu bewogen, den 6804 doch in AS aufzunehmen. Ich wage übrigens keine Spekulationen, welche Abteilung bei Thomson von welcher abgekupfert hat...
Im Gegensatz zur ST62-Version enthält die Include-Datei für den 6804 keine Makros, die die Lücken im Befehlssatz etwas ,,auspolstern'' sollen. Dies überlasse ich dem geneigten Leser als Fingerübung!
Offensichtlich ist es Ehrgeiz jedes Prozessorherstellers, seine eigene Notation für Hexadezimalkonstanten zu erfinden. Texas Instruments war bei diesen Prozessoren besonders originell: ein vorangestelltes >-Zeichen! Die Übernahme dieses Formates in AS hätte zu schweren Konflikten mit den Vergleichs-und Schiebeoperatoren von AS im Formelparser geführt. Ich habe mich deshalb für die Intel-Notation entschieden, zu der sich TI bei der 340x0-Serie und den 3201x-Nachfolgern ja dann auch durchgerungen hat...
Leider hat das Instruktionswort dieser Prozessoren nicht genügend Bits, um bei direkter Adressierung alle 8 Bits zu enthalten, weshalb der Datenadreßraum logisch in 2 Bänke zu 128 Wörtern gespalten ist. AS verwaltet diesen als ein durchgehendes Segment von 256 Wörtern und löscht bei direkten Zugriffen automatisch das Bit 7 (Ausnahme: Befehl SST, der nur in die obere Bank schreiben kann). Der Programmierer ist dafür erforderlich, daß das Bank-Bit stets den richtigen Wert hat!
Ein weiterer, nur sehr versteckt im Datenbuch stehender Hinweis: Die SUBC-Anweisung benötigt zur Ausführung intern mehr als einen Takt, das Steuerwerk arbeitet jedoch schon an dem nächsten Befehl weiter. Im auf ein SUBC folgenden Befehl darf deshalb nicht auf den Akkumulator zugegriffen werden. AS nimmt hier keine Prüfung vor!
Da ich nicht selber diesen Codegenerator geschrieben habe (was nichts an seiner Qualität mindert), kann ich nur kurz hier umreißen, wieso es Befehle gibt, bei denen ein vorangestelltes Label als untypisiert, d.h. keinem Adreßraum zugeordnet, gespeichert wird: Der 20er der TMS-Reihe kennt sowohl ein 64 Kbyte großes Code- als auch Datensegment. Je nach externer Beschaltung kann man dabei Code- und Datenbereiche überlappen, um z.B. Konstanten im Codebereich zu abzulegen und auf diese als Daten zuzugreifen (Ablage im Code ist notwendig, weil ältere AS-Versionen davon ausgehen, daß ein Datensegment aus RAM besteht, das in einem Standalone-System nach dem Einschalten keinen definierten Inhalt hat und verweigern in Segmenten außer Code deshalb die Ablage von Daten). Ohne dieses Feature würde AS nun jeden Zugriff auf die abgelegten Daten mit einer Warnung (,,Symbol aus falschem Segment'') quittieren. Im einzelnen erzeugen folgende Pseudobefehle untypisierte Labels:
BSS, STRING, RSTRING, BYTE, WORD , LONG, FLOATSollten doch einmal typisierte Labels gewünscht sein, so kann man sich behelfen, indem man das Label in eine getrennte Zeile vor dem Pseudobefehl schreibt. Umgekehrt kann man einen der anderen Pseudobefehle mit einem typenlosen Label versehen, indem man vor dem Befehl das Label mit
DOUBLE, EFLOAT, BFLOAT und TFLOAT
<Name> EQU $definiert.
Die größten Magenschmerzen bei diesem Prozessor hat mir die Syntax paralleler Befehle bereitet, die auf zwei Zeilen verteilt werden, wobei beide Befehle an sich auch sequentiell ausgeführt werden können. Deshalb erzeugt AS zuerst den Code für die einzelne erste Operation, wenn er dann in der zweiten Zeile erkennt, daß eine parallele Aweisung vorliegt, wird der zuerst erzeugte Code durch den neuen ersetzt. Im Listing kann man dies daran erkennen, daß der Programmzähler nicht weiterläuft und in der zweiten Zeile anstelle eines Doppelpunktes ein R vor dem erzeugten Code steht.
Bezüglich der doppelten senkrechten Striche und ihrer Position in der Zeile ist man nicht ganz so flexibel wie beim TI-Assembler: Entweder man schreibt sie anstelle eines Labels (d.h. in der ersten Spalte oder mit einem angehängten Doppelpunkt, das ist aber nicht mehr TI-kompatibel...) oder direkt vor den zweiten Befehl ohne Leerzeichen, sonst bekommt der Zeilenparser von AS Probleme und hält die Striche für das Mnemonic.
Wie bei den meisten älteren Prozessorfamilien auch, hatte TI seinerzeit ein eigenes Format zur Schreibweise von Hexadezimal- und Binärkonstanten verwendet, anstelle deren AS die normale, heute auch bei TI gebräuchliche Intel-Notation verwendet.
Die TI-Syntax für Register erlaubt es, daß anstelle eines echten Namens (entweder Rx oder WRx) auch eine einfache Integer-Zahl zwischen 0 und 15 benutzt werden kann. Dies hat zwei Folgen:
Diese Prozessorreihe gehört noch zu den älteren, von TI entwickelten Reihen, und deswegen benutzt TI in ihren eigenen Assemblern noch die herstellereigene Syntax für hexadezimale und binäre Konstanten (vorangestelltes < bzw. ?). Da das in AS aber so nicht machbar ist, wird defaultmäßig die Intel-Syntax verwendet. Auf diese ist Texas bei den Nachfolgern dieser Familie, nämlich den 370ern auch umgestiegen. Beim genaueren Betrachten des Maschinenbefehlssatzes stellt man fest, daß ca. 80% der 7000er-Befehle binär aufwärtskompatibel sind, und auch die Assemblersyntax ist fast gleich - aber eben nur fast. Bei der Erweiterung des 7000er-Befehlssatzes hat TI nämlich auch gleich die Chance genutzt, die Syntax etwas zu vereinheitlichen und zu vereinfachen. Ich habe mich bemüht, einen Teil dieser änderungen auch in die 7000er Syntax einfließen zu lassen:
Obwohl diese Prozessoren keine speziellen Befehle zur Bitmanipulation besitzen, wird mit Hilfe des Assemblers und des DBIT-Befehles (siehe dort) die Illusion erzeugt, als ob man einzelne Bits manipulieren würde. Dazu wird beim DBIT-Befehl eine Adresse mit einer Bitposition zusammengefaßt und in einem Symbol abgelegt, das man dann als Argument für die Pseudobefehle SBIT0, SBIT1, CMPBIT, JBIT0 und JBIT1 verwenden kann. Diese werden in die Befehle OR, AND, XOR, BTJZ und BTJO mit einer passenden Bitmaske übersetzt.
An diesen Bit-Symbolen ist überhaupt nichts geheimnisvolles, es handelt sich um schlichte Integerwerte, in deren unterer Hälfte die Speicheradresse und in deren oberer Hälfte die Bitstelle gespeichert wird. Man könnte sich seine Symbole also auch ohne weiteres selber basteln:
defbit macro name,bit,adr name equ adr+(bit<<16) endmaber mit dieser Schreibweise erreicht man nicht den EQU-artigen Stil, den Texas vorgegeben hat (d.h. das zu definierende Symbol steht anstelle eines Labels). ACHTUNG! Obwohl DBIT eine beliebige Adresse zuläßt, können für die Pseudobefehle nur die Adressen 0..255 und 1000h..10ffh verwendet werden, eine absolute Adressierungsart kennt der Prozessor an dieser Stelle nicht...
Der MSP430 wurde als RISC-Prozessor mit minimalem Stromverbrauch
konzipiert. Aus diesem Grund ist der Satz von Befehlen, die der
Prozessor in Hardware versteht, auf das absolut notwendige reduziert
worden (da RISC-Prozessoren keinen Mikrocode besitzen, muß
jeder Befehl mit zusätzlichem Silizium implementiert werden und
erhöht so den Stromverbrauch). Eine Reihe von Befehlen, die bei
anderen Prozessoren in Hardware gegossen wurden, werden beim MSP
durch eine Emulation mit anderen Befehlen realisiert. Bei AS finden
sich diese Befehle mit in der Datei REGMSP.INC. Wer diese
Datei nicht einbindet, wird bei über der Hälfte der
insgesamt von TI definierten Befehle Fehlermeldungen bekommen!!
Leider Gottes hat sich auch National dazu entschieden, als
Schreibweise für nichtdezimale Integer-Konstanten die von
IBM-Großrechnern bekannte (und von mir vielgehaßte)
Variante X'... zu benutzen. Das geht natürlich (wie immer)
nicht. Zum Glück scheint der ASMCOP aber auch die C-Variante
zuzulassen, und diese wurde deshalb der Default für die COPs...
Wie bei einigen anderen Prozessoren auch, kennt die Assemblersprache
der 75er von NEC Pseudo-Bitoperanden, d.h. man kann einem Symbol eine
Kombination aus Adresse und Bitnummer zuweisen, die dann bei
bitorientierten Befehlen anstelle direkter Ausdrücke verwendet
werden kann. Die drei folgenden Befehle erzeugen daher z.B.
identischen Code:
Die Ablage von Bitsymbolen orientiert sich dabei weitgehend an der
binären Kodierung, die die Prozessorhardware selber verwendet:
Es werden 16 Bit belegt, und es existieren ein ,,kurzes'' und ein
,,langes'' Format. Das kurze Format kann folgende Varianten
aufnehmen:
NEC benutzt in seinen Datenbüchern zur Kennzeichnung der
Zugriffsweise auf absolute Adressen verschiedene Schreibweisen:
Sowohl 7720 als auch 7725 werden von dem gleichen Codegenerator
behandelt und sind sich in ihren Befehlssatz extrem ähnlich.
Trotzdem sollte man sich nicht zu der Annahme verleiten lassen, sie
seien binär kompatibel: Um die längeren Adreßfelder
und zusätzlichen Befehle unterbringen zu können, haben sich
die Bitpositionen einiger Felder im Instruktionswort verschoben, die
Instruktionslänge hat sich auch insgesamt von 23 auf 24 Bit
geändert. Im Code-Format sind deshalb auch unterschiedliche
Header-Ids für beide reserviert.
Gemeinsam ist beiden, daß sie neben Code- und Datensegment auch
noch ein ROM zur Ablage von Konstanten besitzen. Dieses ist bei AS
auf das ROMDATA-Segment abgebildet!
In diesem Kapitel sollen die Formate von von AS erzeugten Dateien
beschrieben werden, deren Format sich nicht direkt erschließt.
Das vom Assembler ausgegebene Codedatenformat muß in der Lage
sein, die Codeteile für unterschiedliche Prozessoren voneinander
zu trennen, und sieht daher etwas anders aus als gängige
Formate. Obwohl dem Assembler Tools zur Bearbeitung der Codedateien
beiliegen, halte ich es für guten Stil, das Format hier kurz
offenzulegen:
Sofern in der Datei Mehrbyte-Integers gespeichert sind, werden sie im
Intelformat abgelegt, d.h. mit dem LSB zuerst. Diese Regel gilt
bereits für das 16-Bit-Kennungswort mit dem Wert $1489, d.h.
jede Codedatei beginnt mit den Bytes $89/$14.
Danach folgt eine Reihe beliebig vieler ,,Records'', wobei ein Record
entweder ein zusammenhängendes Teilfeld des Codes darstellt oder
bestimmte Zusatzinformationen enthält. Eine Datei kann auch ohne
Umschaltung des Prozessortyps mehrere Records enthalten, wenn Code-
oder Konstantenbereiche durch reservierte (und nicht zu
initialisierende) Speicherbereiche unterbrochen werden. Der Assembler
versucht auf diese Weise, die Datei nicht länger als nötig
werden zu lassen.
Allen Records ist gemein ist ein Header-Byte, das den Typ des Records
und die damit folgenden Datenstrukturen festlegt. In einer
Pascal-artigen Form läßt sich die Record-Struktur
folgendermaßen beschreiben:
Ein Record mit einem Header-Byte von $81 ist ein Record, der Code
oder Daten aus beliebigen Segmenten beinhalten kann. Das erste Byte
(Header) gibt an, für welche Prozessorfamilie die folgenden
Daten bzw. der folgende Code bestimmt ist (siehe Tabelle 5.1).
Das Segment-Feld gibt an, in welchen Adreßraum des Prozessors
der folgende Code gehört. Dabei gilt die in Tabelle 5.2 angegeben Zuordnung.
Das Gran-Feld gibt die ,,Granularität'' des Codes an, d.h. die
Größe der kleinsten, adressierbaren Einheit im folgenden
Datensatz. Dieser Wert ist eine Funktion von Prozessortyp und Segment
und ein wichtiges Detail für die Interpretation der beiden
folgenden Felder, die Startadresse und Länge angeben:
Während die Startadresse sich auf die Granularität bezieht,
erfolgt die Längenangabe immer in Bytes! Wäre die
Startadresse z.B. $300 und die Länge 12, so wäre die sich
ergebende Endadresse bei einer Granularität von 1 $30b, bei
einer Granularität von z.B. 4 jedoch $303! Andere
Granularitäten als eins sind selten und treten in erster Linie
bei Signalprozessoren auf, die nicht auf Einzelbyteverarbeitung
ausgelegt sind deren Datenspeicher z.B. aus 64kWorten zu 16 Bit
besteht (DSP56K). Der sich ergebende Speicherplatz beträgt dann
zwar 128 KByte, er ist aber in 2 16 Worten organisiert,
die mit Adressen von 0,1,2,...65535 adressiert werden!
Die Startadresse ist 32-bittig, unabhängig von der
Adreßbreite der jeweiligen Prozessorfamilie. Im Gegensatz dazu
ist die Längenangabe nur 16 Bit lang, ein Record kann also
maximal (4+4+2+(64K-1)) = 65545 Byte lang werden.
Daten-Records mit den Header-Bytes $01..$7f stellen eine
Kurzschreibweise dar und stellen die Abwärtskompatibilität
mit früheren Definitionen des Dateiformats her: Das Header-Byte
gibt direkt den Prozessortyp gemäß der ersten Tabelle an,
das Zielsegment ist auf CODE festgelegt und die
Granularität ergibt sich aus dem Prozessortyp, aufgerundet auf
eine Zweierpotenz von Bytes. AS bevorzugt diese Records, wenn Daten
bzw. Code für das CODE-Segment anstehen.
Der Record mit dem Typ-Byte $80 legt den Einsprungpunkt fest, d.h.
die Adresse, an der mit der Ausführung des Programmes begonnen
werden soll. Ein solcher Record ist das Ergebnis einer
END-Anweisung mit einer entsprechenden Adresse als Argument.
Der letzte Record in der Datei trägt das Header-Byte $00 und
besitzt als einziges Datenfeld einen String, dessen Ende durch das
Dateiende definiert ist. Dieser String spezifiziert, von welchem
Programm diese Datei erzeugt wurde und hat keine weitere Bedeutung.
Debug-Dateien können optional von AS erzeugt werden und liefern
nachgeschalteten Werkzeugen wie Disassemblern oder Debuggern für
diese wichtige Informationen. AS kann Debug-Informationen in drei
Formaten ausgeben: Zum einen im Objekt-Format der AVR-Tools von Atmel
sowie eine zu NoICE kompatible Kommandodatei und zum anderen in einem
eigenen Format. Die ersten beiden werden in [4] bzw. der Dokumentation zu NoICE
ausführlich beschrieben, deshalb beschränkt sich die
folgende Beschreibung auf das AS-eigene MAP-Format:
Diese Informationen in einer MAP-Datei teilen sich in drei Gruppen:
Die Symboltabelle folgt der Quellzeileninformation und ist wieder
primär nach den Segmenten geordnet, aus denen die Symbole
stammen. Im Gegensatz zur Zeileninformation kommt hier allerdings
auch der Abschnitt NOTHING hinzu, der die Symbole
beinhaltet, die keinem speziellen Adreßraum zugeordnet sind
(z.B. Symbole, die einfach mit EQU definiert wurden). Die
Einleitung eines Abschnittes in der Symboltabelle erfolgt mit einer
Zeile der Form
Das erste Feld ist der Name des Symbols selber, eventuell erweitert
um eine in eckigen Klammern eingeschlossene Sektionsnummer, die den
Gültigkeitsbereich des Symbols einschränkt. Die zweite
Spalte bezeichnet den Typ des Symbols: Int für
Integerzahlen, Float für Gleitkommazahlen und
String für Zeichenketten. Die dritte Zeile
schließlich beinhaltet den eigentliche Wert des Symbols. Falls
das Symbol eine Zeichenkette beinhaltet, ist es notwendig, Steuer-
und Leerzeichen mit einer gesonderten Notation zu kennzeichnen, damit
ein im String enthaltenes Leerzeichen nicht eventuell als
Trennzeichen zur nächsten Spalte interpretiert werden kann. AS
bedient sich dazu der bereits der in Assemblerquellen üblichen
Schreibweise, den ASCII-Zahlenwert mit einem führenden Backslash
(\) einzusetzen. Aus dem String
Das vierte Feld gibt - falls vorhanden - die Größe der
Datenstruktur an, die an der durch das Symbol gekennzeichneten
Adresse abgelegt ist. Ein Debugger kann eine solche Information z.B.
nutzen, um symbolisch angesprochene Variablen direkt in der korrekten
Länge aufzulisten. Hat AS keine Informationen über die
Symbolgröße, so steht in diesem Feld eine schlichte -1.
Das fünfte und letzte Feld gibt schlußendlich durch eine 0
oder 1 an, ob das Symbol während der Assemblierung jemals
referenziert wurde. Ein Programm, daß die Symboltabelle liest,
kann auf diese Weise z.B. nicht benutzte Symbole automatisch
verwerfen, da sie beim folgenden Debugging oder der Disassemblierung
mit hoher Wahrscheinlichkeit auch nicht benötigt werden.
Der dritte Abschnitt in einer Debug-Datei beschreibt die im Programm
benutzten Sektionen näher. Eine solche Beschreibung ist
erforderlich, da Sektionen den Gültigkeitsbereich von Symbolen
einschränken können. Je nach momentanem Stand des
Programmzählers kann z.B. ein symbolischer Debugger einzelne
Symboldefinitionen für eine Rückübersetzung nicht
nutzen oder muß Prioritäten bei der Symbolnutzung
beachten. Die Definition einer Sektion beginnt mit einer Zeile der
Form
Programmteile, die außerhalb aller Sektionen liegen, werden
nicht gesondert ausgewiesen. Diese ,,implizite Wurzelsektion''
trägt die Nummer -1 und wird auch als Vatersektion für
Sektionen benutzt, die keine eigentliche Vatersektion besitzen.
Es ist möglich, daß die Datei Leerzeilen oder
Kommentarzeilen (Semikolon am Zeilenanfang) beinhaltet. Diese sind
von einem Leseprogramm zu ignorieren.
Um die Arbeit mit dem Codeformat des Assemblers etwas zu erleichtern,
lege ich einige Progamme zu deren Bearbeitung bei. Für diese
Programme gilt sinngemäß das gleiche wie in 1.1! Allen Programmen gemeinsam sind die
Returncodes, die sie liefern (Tabelle 6.1).
Ebenso einträchtig wie AS lesen sie ihre Eingaben von STDIN und
schreiben Meldungen auf STDOUT (bzw. Fehlermeldungen auf STDERR).
Ein-und Ausgaben sollten sich daher problemlos umleiten lassen.
Sofern Programme im folgenden Zahlen-oder Adreßangaben von der
Kommandozeile lesen, dürfen diese auch hexadezimal geschrieben
werden, indem man sie mit einem voranstehenden Dollarzeichen versieht
(z.B. $10 anstelle von 16).
Unix-Shells ordnen dem Dollarzeichen allerdings eine spezielle
Bedeutung zu (Parameterexpansion), weshalb es nötig ist, einem
Dollarzeichen direkt einen Backslash voranzustellen.
Ansonsten folgen die Aufrufkonventionen und -variationen (bis auf
PLIST und AS2MSG) denen von AS, d.h. man kann dauernd gebrauchte
Schalter in einer Environmentvariablen ablegen (deren Name sich aus
dem Anhängen von CMD an den Programmnamen ergibt, z.B. BINDCMD
für BIND), Optionen negieren und Groß-bzw. Kleinschreibung
erzwingen (näheres zu dem Wie in Abschnitt 2.4).
Sofern Adreßangaben benutzt werden, beziehen sie sich immer auf
die Granularität des Adreßraumes des jeweiligen
Prozessors; beim PIC bedeutet z.B. eine Adreßdifferenz von 1
nicht ein Byte, sondern ein Wort.
PLIST ist das einfachste Programm der vier mitgelieferten; es dient
einfach nur dazu, die in einer Codedatei gespeicherten Records
aufzulisten. Da das Programm nicht allzuviel bewirkt, ist der Aufruf
ziemlich simpel:
ACHTUNG! An dieser Stelle sind keine Jokerzeichen erlaubt!
Falls mit einem Befehl trotzdem mehrere Programmdateien gelistet
werden sollen, kann man sich mit folgendem ''Minibatch'' behelfen:
Zuletzt gibt PLIST noch einen Copyrightvermerk aus, sofern er einen
solchen in der Datei findet, und die Summe aller Codelängen.
PLIST ist praktisch ein DIR für Codedateien. Man kann es
benutzen, um sich den Inhalt einer Datei auflisten zu lassen, bevor
man sie weiterbearbeitet.
BIND ist ein Programm, mit dem man die Records mehrerer Codedateien
in eine Datei zusammenkopieren kann. Die dabei vorhandene
Filterfunktion erlaubt es aber auch, nur Records eines bestimmten
Typs zu übernehmen. Auf diese Weise kann BIND auch dazu
verwendet werden, um eine Codedatei in mehrere aufzuspalten.
Die allgemeine Syntax von BIND lautet
An Optionen definiert BIND momentan nur eine:
P2HEX ist eine Erweiterung von BIND. Es besitzt alle
Kommandozeilenoptionen von BIND und hat die gleichen Konventionen
bzgl. Dateinamen. Im Gegensatz zu BIND wird die Zieldatei aber als
Hexfile ausgegeben, d.h. als eine Folge von Zeilen, die den Code als
ASCII-Hexzahlen enthalten.
P2HEX kennt 8 verschiedene Zielformate, die über den
Kommandozeilenparameter F ausgewählt werden können:
Finden sich Code-Records verschiedener Prozessoren in einer
Quelldatei, so erscheinen die verschiedenen Hexformate auch gemischt
in der Zieldatei --- es empfiehlt sich also dringend, von der
Filterfunktion Gebrauch zu machen.
Neben dem Codetypenfilter kennt P2HEX noch ein Adreßfilter, das
nützlich ist, falls der Code auf mehrere EPROMs verteilt werden
muß:
ACHTUNG! Die Splittung ändert nichts an den absoluten
Adressen, die in den Hexfiles stehen! Sollen die Adressen im Hexfile
bei 0 beginnen, so kann man dies durch den zusätzlichen Schalter
Als Sonderwerte für Start-und Endadresse beim r-Parameter ist
ein schlichtes Dollar-Zeichen ($) erlaubt. Diese kennzeichnet die
erste bzw. letzte in der Programmdatei belegte Adresse. Wer also
sicher sein will, daß immer das ganze Programm in der Hex-Datei
abgelegt wird, braucht sich mit dem Schalter
Den Inhalt einer Datei kann man mit einem Offset auf eine beliebige
Position verschieben; diesen Offset hängt man einfach in
Klammern an den Dateinamen an. Ist der Code in einer Datei z.B. auf
Adresse 0 in der P-Datei abgelegt, man möchte ihn jedoch auf
Adresse 1000h verschieben, so hängt man an ($1000) an
den Dateinamen (ohne Leerzeichen!) an.
Da das TI-DSK-Format Daten und Code unterscheiden kann,
läßt sich mit dem Schalter
Leider ist sich die Literatur nicht ganz über die Endezeile
für Intel-Hexfiles einig. P2HEX kennt daher 3 Varianten,
einstellbar über den Parameter i mit einer nachfolgenden
Ziffer:
Defaultmäßig wird die Variante 0 benutzt, die die
gebräuchlichste zu sein scheint.
Fehlt der Zieldateiangabe eine Endung, so wird HEX als
Endung angenommen.
Defaultmäßig gibt P2HEX pro Zeile maximal 16 Datenbytes
aus, wie es auch die meisten anderen Tools tun, die Hex-Files
erzeugen. Wollen Sie dies ändern, so können Sie dies mit
dem Schalter
Meist werden die temporären, von AS erzeugten Code-Dateien nach
einer Umwandlung nicht mehr unbedingt gebraucht. Mit der
Kommandozeilen- option
Anders als BIND erzeugt P2HEX keine Leerdatei, wenn nur ein Dateiname
(=Zieldatei) angegeben wurde, sondern bearbeitet die
dazugehörige Codedatei. Es ist also ein Minimalaufruf à
la
P2BIN funktioniert wie P2HEX und bietet die gleichen Optionen (bis
auf die a- und i- Optionen, die bei Binärdateien keinen Sinn
ergeben), nur wird das Ergebnis nicht als Hexdatei, sondern als
einfache Binärdatei abgelegt. Dies kann dann z.B. direkt in ein
EPROM gebrannt werden.
Zur Beeinflussung der Binärdatei kennt P2BIN gegenüber
P2HEX noch drei weitere Optionen:
Nicht wundern: Bei letzteren Optionen ist die Binärdatei um den
Faktor 2 oder 4 kleiner als bei ALL. Dies ist bei konstantem
Adreßfenster logisch!
Falls die Code-Datei keine Startadresse enthält, kann man diese
analog zu P2HEX über die -e-Kommandozeilenoption
vorgeben. Auf Anforderung teilt P2BIN ihren Wert der Ergebnisdatei
voran. Mit der Kommandozeilenoption
Bei AS2MSG handelt es sich eigentlich um kein Hilfsprogramm, sondern
um ein Filter, das (glücklichen) Besitzern von Borland-Pascal
7.0 das Arbeiten mit dem Assembler erleichtern soll. In den
DOS-Arbeitsumgebungen existiert ein ,,Tools''-Menü, das man um
eigene Programme, z.B. AS erweitern kann. Das Filter erlaubt, die von
AS gelieferten Fehlermeldungen mit Zeilenangabe direkt im
Editorfenster anzuzeigen. Dazu muß im Tools-Menü ein neuer
Eintrag angelegt werden (Options/Tools/New). Tragen Sie in
die einzelnen Felder folgende Werte ein :
Ich setze dabei voraus, daß sowohl AS als auch AS2MSG sich in
einem Verzeichnis befinden, welches in der Pfadliste aufgeführt
ist. Nach einem Druck auf dem passenden Hotkey (oder Auswahl aus dem
Tools-Menü) wird AS mit dem Namen der Textdatei im aktiven
Editorfenster aufgerufen. Die dabei aufgetretenen Fehler werden in
ein separates Fenster geleitet, durch das man nun ,,browsen'' kann.
Mit Ctrl-Enter springt man eine fehlerhafte Zeile an.
Zusätzlich enthält das Fenster die Statistik, die AS am
Ende der Assemblierung ausgibt. Diese erhalten als Dummy-Zeilennummer
1.
Für diese Arbeitsweise sind sowohl TURBO.EXE (Real Mode) als
auch BP.EXE (Protected Mode) geeignet. Ich empfehle BP, da in dieser
Variante beim Aufruf nicht erst der halbe DOS-Speicher
,,freigeswappt'' werden muß.
Im folgenden findet sich eine halb-tabellarische Auflistung der in AS
definierten Fehlermeldungen. Zu jeder Fehlermeldung finden sich
folgende Angaben:
Die hier aufgelisteten Fehlermeldungen werden nicht nur von AS bei
E/A- Fehlern ausgegeben, sondern auch von den Hilfsprogrammen PLIST,
BIND, P2HEX und P2BIN. Es sind nur die Fehler näher
erklärt, die m.E. bei der Arbeit auftreten können. Sollte
doch einmal ein nicht erläuterter E/A-Fehler auftreten, so
dürfte der Grund in einem Programmfehler liegen. Melden Sie dies
unbedingt!!
In diesem Kapitel habe ich versucht, einige besonders häufig
gestellte Fragen mit den passenden Antworten zu sammeln. Die
Antworten auf die hier auftauchenden Probleme finden sich zwar auch
an anderer Stelle in der Anleitung, jedoch findet man sie vielleicht
nicht auf den ersten Blick...
In diesem Anhang finden sich noch einmal als schnelle Referenz alle
von AS zur Verfügung gestellten Pseudobefehle. Die Liste ist in
zwei Teile gegliedert: Im ersten Teil finden sich Befehle, die
unabhängig vom eingestellten Zielprozessor vorhanden sind,
danach folgen für jede Prozessorfamilie die zusätzlich
vorhandenen Befehle:
Boolean-Symbole sind eigentlich normale normale Integer-Symbole, mit
dem Unterschied, daß ihnen von AS nur zwei verschiedene Werte
(0 oder 1, entsprechend FALSE oder TRUE) zugewiesen werden.
Spezialsymbole werden von AS nicht in der Symboltabelle abgelegt,
sondern aus Geschwindigkeitsgründen direkt im Parser abgefragt.
Sie tauchen daher auch nicht in der Symboltabelle des Listings auf.
Während vordefinierte Symbole nur einmal am Anfang eines Passes
besetzt werden, können sich die Werte dynamischer Symbole
während der Assemblierung mehrfach ändern, da sie mit
anderen Befehlen vorgenommene Einstellungen widerspiegeln.
Die hier aufgelistete Schreibweise ist diejenige, mit der man die
Symbole auch im case-sensitiven Modus erreicht.
Die hier aufgeführten Namen sollte man für eigene Symbole
meiden; entweder kann man sie zwar definieren, aber nicht darauf
zugreifen (bei Spezialsymbolen), oder man erhält eine
Fehlermeldung wegen eines doppelt definierten Symboles. Im gemeinsten
Fall führt die Neubelegung durch AS zu Beginn eines Passes zu
einem Phasenfehler und einer Endlosschleife...
Der Distribution von AS liegen eine Reihe von Include-Dateien bei.
Neben Includes, die sich nur auf eine Prozessorfamilie beziehen (und
deren Funktion sich demjenigen unmittelbar erschließt, der mit
dieser Familie arbeitet), existieren aber auch ein paar Dateien, die
prozessorunabhängig sind und die eine Reihe nützlicher
Funktionen implementieren. Die definierten Funktionen sollen hier
kurz beschrieben werden:
Diese Datei definiert eine Reihe bitorientierter Operationen, wie man
sie bei anderen Assemblern vielleicht fest eingebaut sind. Bei AS
werden sie jedoch mit Hilfe benutzerdefinierter Funktionen
implementiert:
Dieser Include ist das Pendant zu dem bei C vorhandenen Header
ctype.h, der Makros zur Klassifizierung von Zeichen anbietet.
Alle Funktionen liefern entweder TRUE oder FALSE:
Wenn man sich entschließt, ein solches Kapitel neu zu
schreiben, nachdem es eigentlich schon zwei Jahre veraltet ist,
läuft man automatisch Gefahr, daß dabei der eine oder
andere gute Geist, der etwas zum bisherigen Gelingen dieses Projektes
beigetragen hat, vergessen wird. Der allererste Dank gebührt
daher allen Personen, die ich in der folgenden Aufzählung
unfreiwillig unterschlagen habe!
AS als Universalassembler, wie er jetzt besteht, ist auf Anregung von
Bernhard (C.) Zschocke entstanden, der einen
,,studentenfreundlichen'', d.h. kostenlosen 8051-Assembler für
sein Mikroprozessorpraktikum brauchte und mich dazu bewegt hat, einen
bereits bestehenden 68000-Assembler zu erweitern. Von dortan nahm die
Sache ihren Lauf... Das Mikroprozessorpraktikum an der RWTH Aachen
hat auch immer die eifrigsten Nutzer der neuesten AS-Features (und
damit Bug-Sucher) gestellt und damit einiges zur jetzigen
Qualität von AS beigetragen.
Das Internet und FTP haben sich als große Hilfe bei der Meldung
von Bugs und der Verbreitung von AS erwiesen. Ein Dank geht daher an
die FTP-Administratoren (Bernd Casimir in Stuttgart, Norbert Breidohr
in Aachen und Jürgen Meißburger in Jülich).
Insbesondere letzterer hat sich sehr engagiert, um eine praxisnahe
Lösung im ZAM zu finden.
Ach ja, wo wir schon im ZAM sind: Wolfgang E. Nagel hat zwar nichts
direkt mit AS zu tun, immerhin ist er aber mein Chef und wirft
ständig vier Augen auf das, was ich tue. Bei AS scheint
zumindest ein lachendes dabei zu sein...
Ohne Datenbücher und Unterlagen zu Prozessoren ist ein Programm
wie AS nicht zu machen. Ich habe von einer enormen Anzahl von Leuten
Informationen bekommen, die von einem kleinen Tip bis zu ganzen
Datenbüchern reichen. Hier eine Aufzählung (wie oben
gesagt, ohne Garantie auf Vollständigkeit!):
Ernst Ahlers, Charles Altmann, Rolf Buchholz, Bernd Casimir, Gunther
Ewald, Stephan Hruschka, Peter Kliegelhöfer, Ulf Meinke,
Matthias Paul, Norbert Rosch, Steffen Schmid, Leonhard Schneider,
Michael Schwingen, Oliver Sellke, Christian Stelter, Oliver Thamm,
Thorsten Thiele.
...und ein gehässiger Dank an Rolf-Dieter-Klein und Tobias
Thiel, die mit ihren ASM68K demonstrierten, wie man es nicht
machen sollte und mich damit indirekt dazu angeregt haben, etwas
besseres zu schreiben!
So ganz allein habe ich AS nicht verzapft. AS enthält die
OverXMS-Routinen von Wilbert van Leijen, um die Overlay-Module ins
Extended Memory verlagern zu können. Eine wirklich feine Sache,
einfach und problemlos anzuwenden!
Die TMS320C2x/5x-Codegeneratoren sowie die Datei
STDDEF2x.INC stammen von Thomas Sailer, ETH Zürich.
Erstaunlich, an einem Wochenende hat er es geschafft, durch meinen
Code durchzusteigen und den neuen Generator zu implementieren.
Entweder waren das reichliche Nachtschichten oder ich werde langsam
alt...
Wie in der Einleitung erwähnt, gebe ich nach Rücksprache
den Quellcode von AS heraus. Im folgenden sollen einige Hinweise zu
dessen Handhabung gegeben werden.
AS ist in Turbo-Pascal implementiert worden. ,,Aua'', höre ich
jetzt die C-Freaks schreien, ,,in C geht so etwas doch viel besser,
außerdem ist es dann beliebig portabel!'' Jaaaa, wenn das alles
so einfach wäre...AS ist ein Projekt, das ich nun schon seit
einigen Jahren verfolge, und zu der Zeit, als ich damit anfing,
bestand die Betriebssystemwelt des PC-Benutzers aus DOS, DOS und DOS,
und die Erfahrung, daß ich ohne Festplatte schon bei Turbo-C
mit 4 Disketten jonglieren mußte, Turbo-Pascal aber immer noch
auf eine paßte, wirkte noch sehr deutlich nach. Auch das
Dickicht der Speichermodelle eines DOS-C-Compilers machte die Sache
nicht attraktiver. Außerdem kannte ich mich mit Turbo-Pascal am
besten aus, und es war die Sprache, in der ich am produktivsten war.
C hatte ich durchaus schon einmal ausprobiert, und es machte auf mich
einen chaotischen und archaischen Eindruck:
Ein nicht ganz unwichtiger Hinweis für Anwender der Version 7.0
von Turbo- oder Borland-Pascal: Wie sich schon länger
herumgesprochen hat, hat diese Version einige Bugs, weshalb Borland
auch gezwungen war, die Version 7.01 herauszugeben. Bei AS tritt das
Problem auf, daß teilweise Longint-Schiebebefehle mit einer
Amplitude von mehr als 16 verwendet werden, für die die Version
7.0 eine fehlerhafte 386-Optimierung enthält. Wenn Sie (wie ich)
nicht das Geld für den Update auf 7.01 ausgeben wollen und die
Quellen der Laufzeitbibliothek besitzen, können Sie den Fehler
auch selber beseitigen, indem Sie die beiden fehlerhaften Routinen in
LONG.ASM austauschen, z.B. so:
Programme in der Größe von AS müssen notwendigerweise
in mehrere Module aufgespalten werden, nicht nur um eine
vernünftige Strukturierung zu erreichen, sondern auch, um unter
DOS die ewige 64 Kbyte-Grenze zu überwinden. Im einzelnen
besteht AS aus folgenden Modulen:
Dies ist im eigentlichen Sinne kein Modul, sondern eine
Include-Datei, die von allen anderen Modulen eingebunden wird. Sie
enthält die unvermeidlichen Compiler-Schalter, die sich je nach
Zielplattform etwas unterscheiden.
Diese Datei enthält das Hauptmodul von AS und muß
demzufolge in der IDE als Hauptdatei eingetragen werden. Sie
beinhaltet die übergeordnete Steuerung der einzelnen
Durchläufe, das Einlesen der Quelldateien sowie Teile des
Makroprozessors. Dieser Programmteil ist unabhängig vom
Zielprozessor.
Dieses Modul enthält lediglich Deklarationen von überall
benötigten Konstanten und gemeinsam benutzten Variablen.
Hier finden sich gesammelt einige häufig gebrauchte
Unterroutinen, welche in erster Linie die Bereiche Stringbearbeitung
und Fehlerbehandlung abdecken.
Hier geht es ins Eingemachte: In diesem Modul werden die
Symboltabellen (global und lokal) in zwei Binärbäumen
verwaltet. Außerdem findet sich hier eine ziemlich große
Prozedur EvalExpression, welche einen (Formel-)ausdruck
analysiert und auswertet. Die Prozedur liefert das Ergebnis (Integer,
Gleitkomma oder String) in einem varianten Record zurück. Zur
Auswertung von Ausdrücken bei der Codeerzeugung sollten
allerdings eher die Funktionen EvalIntExpression,
EvalFloatExpression und EvalStringExpression verwendet
werden. Änderungen zum Einfügen neuer Prozessoren sind hier
nicht erforderlich und sollten auch nur mit äußerster
Überlegung erfolgen, da man hier sozusagen an ,,die Wurzel'' von
AS greift.
In diesem Modul finden sich die Routinen zur Speicherung und Abfrage
von Makros. Der eigentliche Makroprozessor befindet sich in AS.PAS!!
Hier befinden sich alle Routinen, die die bedingte Assemblierung
steuern. Exportiert wird als wichtigste Variable das Flag
IfAsm, welches anzeigt, ob Codeerzeugung momentan ein- oder
ausgeschaltet ist.
In diesem Modul befindet sich die Verwaltung der Code-Ausgabedatei.
Exportiert wird ein Interface, mit dem sich eine Code-Datei
öffnen und schließen läßt, und das Routinen zum
Einschreiben (und Zurücknehmen) von Code anbietet. Eine wichtige
Aufgabe dieses Moduls ist die Pufferung des Schreibvorgangs, die die
Ausgabegeschwindigkeit erhöht, indem der erzeugte Code in
größeren Blöcken geschrieben wird.
In diesem Modul werden all die Befehle bearbeitet, die für alle
Prozessoren definiert sind, z.B. EQU und ORG. Hier
findet sich auch der CPU-Befehl, mit dem zwischen den
einzelnen Prozessoren hin- und hergeschaltet wird.
Hier finden sich Pseudobefehle, die von mehreren Codegeneratoren
verwendet werden. Dies ist einmal die Intel-Gruppe mit der
DB..DT-Gruppe, zum anderen die Pendants für die 8/16-Bitter
von Motorola oder Rockwell. Wer in diesem Bereich um einen Prozessor
erweitern will, kann mit einem Aufruf den größten Teil der
Pseudobefehle erschlagen.
Dieses Modul implementiert den Mechanismus der Kommdozeilenparameter.
Es benötigt eine Spezifikation der erlaubten Parameter, zerlegt
die Kommadozeile und ruft die entsprechenden Callbacks auf. Der
Mechanismus leistet im einzelnen folgendes:
Dieses mit Abstand kürzeste Modul dient nur einem einzigen Zweck
(und ist vielleicht auch in anderen Programmen nützlich). Es
ergänzt Pascal um die von C her bekannte Möglichkeit, auf
die neben Standardeingabe und -ausgabe von DOS vordefinierten
Kanäle
Hiermit wird ein Thema abgehandelt, das sowohl von Borland als auch
den meisten Programmierern eher stiefmütterlich behandelt wird:
der National Language Support (kurz NLS), den es bei DOS schon seit
Version 2.0 gibt. Diese Unit liefert die Informationen über
Dinge, die sich von Land zu Land unterscheiden:
Dies ist nur ein kleiner ,,Hack'', der Routinen zur Verwaltung von
linearen Listen mit Strings als Inhalt definiert, welche z.B. im
Makroprozessor von AS gebraucht werden.
Hier sind einige häufig genutzte String-Operationen gelandet.
Dieses Modul definiert einen Datentyp, mit dem eine Liste von
Adreßbereichen verwaltet werden kann. Dies Funktion wird von AS
für die Belegungslisten benötigt, außerdem benutzten
P2BIN und P2HEX diese Listen, um vor Überlappungen zu warnen.
In diesem Modul ist die Listenstruktur definiert, über die AS
die Verschachtelung von Include-Dateien im Listing ausgeben kann.
Dateinamen werden von AS an vielen Stellen tokenisiert, d.h. in Form
numerischer Codes gespeichert. In diesem Modul werden die Listen
verwaltet, die eine Hin-und Herwandlung zwischen Dateinamen und
Tokens ermöglichen.
Diese Dateien schlußendlich enthalten die eigentlichen
Codegeneratoren für die verschiedenen Prozessoren. Sie sind alle
nach dem gleichen Schema aufgebaut (natürlich mit
unterschiedlichem Inhalt!), das im folgenden Abschnitt erklärt
wird.
Der mit Abstand häufigste Grund, im Quellcode von AS etwas zu
verändern, dürfte wohl die Erweiterung um einen neuen
Zielprozessor sein. Das Verfahren der Definition eines neuen
Prozessors hat sich mit V1.39p4 drastisch geändert: Es ist
wesentlich indirekter geworden, hat aber den Vorteil, daß in
den Standard-Modulen von AS nur noch an EINER Stelle etwas
geändert werden muß. Es beruht sehr stark auf indirekten
Verweisen und Prozedurvariablen, verlangt also schon etwas vertiefte
Kenntnisse von (Turbo-)Pascal. Wer aber in Assembler programmiert,
sollte damit keine grundsätzlichen Probleme haben. Im folgenden
will ich kochbuchartig die zum Einhängen erforderlichen Schritte
beschreiben:
Der für den Prozessor zu wählende Name muß zwei
Kriterien erfüllen:
Die Unit, die für den neuen Prozessor zuständig sein soll,
sollte einer gewissen Einheitlichkeit wegen den Namen
CODExxxx tragen, wobei xxxx etwas mit dem
Prozessornamen zu tun haben sollte. Den Kopf mit den
Compilerschaltern sowie den Uses-Anweisungen übernimmt
man am besten direkt aus einer bereits vorhandenen
Codegenerator-Unit.
Die Unit selber muß weder Variablen noch Prozeduren oder
Funktionen nach außen exportieren, da die ganze Kommunikation
zur Laufzeit über indirekte Sprünge abgewickelt wird. Die
dazu erforderlichen Initialisierungen müssen im
Initialisierungsteil der Unit vorgenommen werden, indem für
jeden von der Unit zu behandelnden Prozessortyp ein Aufruf der
Funktion AddCPU erfolgt:
Dem Umschalter obliegt es, AS auf den neuen Zielprozessor
,,umzupolen''. Dazu müssen im Umschalter einige globale
Variablen besetzt werden:
Neben diesen Variablen sind noch drei Prozedurvariablen zu besetzen,
die die Verbindung von AS zu den ,,aktiven'' Teilen des
Codegeneratormoduls herstellen:
Bei Bedarf kann sich die Unit im Initialisierungsteil noch in die
Kette aller Funktionen eintragen, die vor Beginn eines Durchlaufes
durch den Quelltext ausgeführt werden. Dies ist z.B. immer dann
der Fall, wenn die Code-Erzeugung im Modul abhängig vom Stand
bestimmter, durch Pseudobefehle beeinflußbarer Flags ist. Ein
häufig auftretender Fall ist z.B., daß ein Prozessor im
User- oder Supervisor-Modus arbeiten kann, wobei im User-Modus
bestimmte Befehle gesperrt sind. Im Assembler-Quelltext könnte
dieses Flag, das angibt, in welchem Modus der folgende Code
ausgeführt wird, durch einen Pseudobefehl umgeschaltet werden.
Es ist aber dann immer noch eine Initialisierung erforderlich, die
sicherstellt, daß in allen Durchläufen ein identischer
Ausgangszustand vorliegt. Der über die Prozedurvariable
InitPassProc angebotene Haken bietet die Möglichkeit,
derartige Initialisierungen vorzunehmen. Das verwendete Prinzip
ähnelt dabei dem Einhängen in einen Interruptvektor: In der
Initialisierung der Unit wird der alte Wert von InitPassProc
gesichert. Danach kann InitPassProc auf die
hinzuzufügende Routine (parameterlose, FAR deklarierte
Prozedur) umgebogen werden. Die neue Routine ruft dann zuerst die
alte Initialisierungsroutine auf und führt danach ihre eigenen
Operationen durch.
Analog zu InitPassProc funktioniert die über
CleanUpProc aufgebaute Prozedurkette, die es den Codegeneratoren
erlaubt, nach dem Abschluß der Assemblierung noch
Aufräumarbeiten (z.B. das Freigeben von Literaltabellen
o.ä.) durchzuführen. Dies ist sinnvoll, wenn mehrere
Dateien mit einem Aufruf assembliert werden, sonst hätte man
noch ,,Müll'' aus einem vorigen Lauf in den Tabellen. Momentan
nutzt kein Modul diese Möglichkeit.
Bisweilen sind bei einem bestimmten Prozessor Symbole definiert, ohne
daß man sie irgendwo explizit erzeugen müßte. Ein
Beispiel ist z.B. die TMS370-Reihe, bei der z.B. die ersten 256
Speicherzellen als ,,Register'' R0..R255 bzw. R0FF
angesprochen werden können. Dafür existiert in AS eine
Prozedurvariable InternSymbol, die vom Codegenerator belegt
werden kann. Wann immer der Formelparser einen Ausdruck analysieren
muß, ruft er diese Routine nach der Untersuchung auf Konstanten
hin auf. Falls die Routine ein vordefiniertes Symbol erkennt,
muß sie das Ergebnis in einen übergebenen Record des Typs
TempResult eintragen, d.h. das Feld Typ mit der Art des
Ergebnisses (TempInt für Integer, TempFloat
für Gleitkomma oder TempString für Strings)
belegen sowie das eigentliche Ergebnis in eines der Felder Int,
Float oder Ascii eintragen. War die Untersuchung
erfolglos, so schreiben Sie einfach TempNone in das Feld
Typ. Fehlermeldungen aus dieser Routine heraus sollten vermieden
werden, da nicht zu identifizierende Namen normale Symbole bezeichnen
könnten (was der Parser dann im folgenden abprüft). Seien
Sie vorsichtig mit dieser Routine, da Sie mit ihr mitten in den
Parser hineingreifen können!
Wer will, kann sich übrigens auch mit einem Copyright-Eintrag
verewigen, indem er in der Initialisierung der Unit (bei den
AddCPU-Befehlen) einen Aufruf der Prozedur AddCopyright
einfügt, in der folgenden Art:
So schwierig die formal richtige Definition des Moduls ist, so
trivial wird dadurch seine Einbindung: Im Hauptmodul AS.PAS muß
lediglich die USES-Liste um dieses Modul erweitert werden.
Ob diese Modul auch als Overlay definiert werden soll, ist eine
Abwägung zwischen Geschwindigkeit und freiem Speicherplatz.
Hier ist nun endlich eigene Kreativität gefragt: Wie Sie es
schaffen, aus dem Mnemonic und den Argumenten die Code-Bytes zu
erzeugen, ist weitgehend Ihnen überlassen. Zur Verfügung
stehen dafür natürlich über den Formelparser die
Symboltabellen sowie die Routinen aus ASMSUB und
ASMPARS. Ich kann hier nur einige generelle Hinweise geben:
Damit PLIST Codedateien weiterhin vollständig anzeigen
kann, muß das Feld mit den vorhandenen Code-Headern erweitert
werden.
Sollte ein Segment eine andere Granularität als 1 Byte/Adresse
aufweisen (s. die Variable Grans oben), so muß die
Funktion Granularity in TOOLS.PAS erweitert werden.
Damit P2HEX sich nicht mit einer Fehlermeldung abmeldet,
muß noch entschieden werden, welches Hex-Format
defaultmäßig für diesen Prozessor verwendet werden
soll. P2HEX kennt bisher Motorola S-Records (bis 32-Bit-Adressen)
sowie Intel-, Tektronix- und MOS-HEX (16-Bit-Adressen). Die
CASE-Abfrage in ProcessFile muß nur entsprechend
erweitert werden.
Falls es einmal erforderlich sein sollte, AS auf eine andere Sprache
anzupassen, sind sämtliche String-Konstanten im Programm zu
ändern. Damit dies einigermaßen leicht möglich ist,
finden sich diese konzentriert in den RSC-Dateien. IOERRORS.RSC
enthält alle E/A-Fehlermeldungen und wird sowohl von AS als auch
den Dienstprogrammen genutzt. Ansonsten hat jedes Programm seine
eigene Datei. Solange Sie nicht vorhaben, AS in eine andere Sprache
zu übersetzen, brauchen Sie sich nicht weiter um diese Dateien
zu kümmern.
4.40. MSP430
4.41. COP8 & SC/MP
4.42. 75K0
ADM sfr 0fd8h
SOC bit ADM.3
skt 0fd8h.3
skt ADM.3
skt SOC
AS unterscheidet direkte und symbolische Bitzugriffe an einem bei
Symbolen fehlenden Punkt; Punkte in Symbolnamen darf man daher nicht
verwenden, da es sonst zu Mißverständnissen bei der
Auflösung kommt.
Das obere Byte ist auf 0 gesetzt, das untere Byte enthält den
gemäß [56] kodierten
Bitausdruck. Das lange Format kennt im Gegensatz dazu nur direkte
Adressierung, kann dafür aber (korrekte Einstellungen von
MBS und MBE vorausgesetzt) den ganzen Adreßraum
abdecken. Bei langen Ausdrücken stehen im unteren Byte Bit 7..0
der Adresse, in Bit 8 und 9 die Bitstelle sowie in Bit 10 und 11
konstant 01. Letztere ermöglichen es, langes und kurzes Format
einfach durch einen Vergleich des oberen Bytes gegen Null zu
unterscheiden. Die Bits 12..15 enthalten Bit 8..11 der Adresse; sie
werden zwar nicht zur Generierung des Kodes benötigt,
müssen jedoch gespeichert werden, da eine Prüfung auf ein
korrektes Banking erst bei der Verwendung des Symboles erfolgen kann.
4.43. 78K0
Bei AS sind diese Präfixe nur notwendig, falls man eine
bestimmte Adressierung erzwingen will und der Befehl verschiedene
Varianten zuläßt. Setzt man keinen Präfix, so
wählt AS automatisch die kürzeste Variante. Es dürfte
daher in der Praxis sehr selten notwendig sein, einen Präfix zu
verwenden.
5.1. Code-Dateien
FileRecord = RECORD CASE Header:Byte OF
$00:(Creator:ARRAY[] OF Char);
$01..
$7f:(StartAdr : LongInt;
Length : Word;
Data : ARRAY[0..Length-1] OF Byte);
$80:(EntryPoint:LongInt);
$81:(Header : Byte;
Segment : Byte;
Gran : Byte;
StartAdr : LongInt;
Length : Word;
Data : ARRAY[0..Length-1] OF Byte);
END
Was in dieser Schreibweise nicht ganz zum Ausdruck kommt, ist,
daß die Länge von Datenfeldern variabel ist und von
Length abhängt.
Header
Familie
Header
Familie
$01
$05
$11
$13
$19
$25
$2a
$32
$39
$3b
$3f
$42
$48
$4a
$51
$53
$55
$61
$63
$65
$68
$6c
$6f
$71
$73
$75
$77
$79
$7b
$7d
$7f680x0, 6833x
PowerPC
65xx/MELPS-740
M16
65816/MELPS-7700
SYM53C8xx
i960
ST9
MCS-96/196/296
AVR
4004/4040
8086..V35
TMS9900
MSP430
Z80/180/380
TLCS-90
TLCS-47
6800, 6301 oder 6811
6809
68HC16
H8/300(H)
SH7000
COP8
PIC16C5x
TMS-7000
TMS320C2x
TMS320C5x
Z8
75K0
µPD7720
µPD77230$03
$09
$12
$14
$21
$29
$31
$33
$3a
$3c
$41
$47
$49
$4c
$52
$54
$56
$62
$64
$66
$69
$6e
$70
$72
$74
$76
$78
$7a
$7c
$7e
M*Core
DSP56xxx
MELPS-4500
M16C
MCS-48
29xxx
MCS-51
ST7
8X30x
XA
8080/8085
TMS320C6x
TMS370xxx
80C166/167
TLCS-900
TLCS-870
TLCS-9000
6805/HC08
6804
68HC12
H8/500
SC/MP
PIC16C8x
PIC17C4x
TMS3201x
TMS320C3x
ST6
µPD78(C)10
78K0
µPD7725
Nummer
Segment
Nummer
Segment
$00
$02
$04
$06
$08<undefiniert>
DATA
XDATA
BDATA
REG$01
$03
$05
$07
$09CODE
IDATA
YDATA
IO
ROMDATA5.2. Debug-Dateien
Letzterer Teil findet sich zuerst in der Datei. Ein einzelner Eintrag
in dieser Liste besteht aus zwei, von einem Doppelpunkt getrennten
Zahlen:
<Zeilennummer>:<Adresse>
Ein solcher Eintrag besagt, daß der aus einer bestimmten
Quellcodezeile erzeugte Maschinencode auf der angegebenen Adresse
(hexadezimal) zu liegen kam. Mit einer solchen Information kann ein
Debugger beim Durchsteppen des Programmes die entsprechenden
Quellcodezeilen anzeigen. Da ein Programm aber auch aus mehreren
Include-Dateien bestehen kann, und viele Prozessoren mehr als nur
einen Adreßraum besitzen (von dem zugegebenermaßen nur in
einem Code liegt), müssen die oben beschriebenen Einträge
sortiert werden. AS tut dies in zwei Stufen: Das primäre
Sortierkriterium ist das Zielsegment, innerhalb dieser Segmente wird
noch einmal nach Dateien sortiert. Einzelne Abschnitte werden dabei
durch durch spezielle Zeilen der Form
Segment <Segmentname>
bzw.
File <Dateiname>
getrennt.
Symbols in Segment <Segmentname> .
Innerhalb eines Abschnittes sind die Symbole nach Namen sortiert, und
ein Symboleintrag belegt genau eine Zeile. Eine solche Zeile besteht
wiederum aus 5 Feldern, die durch jeweils mindestens ein Leerzeichen
getrennt sind:
Dies ist ein Test
wird also z.B.
Dies\032ist\032ein\032Test
Die Zahlenangabe ist immer dezimal und dreistellig, und der Backslash
selber wird ebenfalls in dieser Schreibweise kodiert.
Info for Section nn ssss pp ,
wobei nn die Nummer der Sektion angibt (die Nummer, die als
Postfix für Symbolnamen in der Symboltabelle genutzt wird),
ssss der Name der Sektion ist und pp die Nummer der
Vatersektion darstellt. Letztere Information benötigt ein
Rückübersetzer, um sich bei der Auffindung eines Symbols
für einen Zahlenwert ausgehend von der aktuellen Sektion im Baum
bis zur Wurzel ,,durchhangeln'' kann, bis ein passendes Symbol
gefunden wird. Auf diese Zeile folgt eine Reihe weiterer Zeilen, die
den von dieser Sektion belegten Code-Bereich beschreiben. Jeder
einzelne Eintrag (genau einer pro Zeile) beschreibt entweder eine
einzelne Adresse oder einen durch zwei Grenzwerte beschriebenen
Bereich (Trennung von Anfangs-und Endwert durch ein Minuszeichen).
Die Grenzen sind dabei ,,inklusive'', d.h. die Grenzen gehören
auch zu dem Bereich. Wichtig ist, daß ein einer Sektion
zugehöriger Bereich nicht nochmals für ihre Vatersektionen
aufgeführt wird (eine Ausnahme ist natürlich, wenn Bereiche
absichtlich mehrfach belegt werden, aber so etwas macht man ja auch
nicht, gelle?). Dies dient einer Optimierung der Bereichsspeicherung
während der Assemblierung und sollte auch für eine
Symbolrückübersetzung keine Probleme darstellen, da durch
die einfache Kennzeichnung bereits der Einstiegspunkt und damit der
Suchpfad im Sektionsbaum gegeben ist. Die Beschreibung einer Sektion
wird durch eine Leerzeile oder das Dateiende gekennzeichnet.
6. Hilfsprogramme
Returncode
tritt auf bei...
0
1
2
3kein Fehler
Kommandozeilenparameterfehler
I/O-Fehler
Dateiformatfehler
PLIST $<$Dateiname$>$
Der Dateiname wird automatisch um die Endung P erweitert, falls keine
Endung vorhanden ist.
for %n in (*.p) do plist %n
PLIST gibt den Inhalt der Codedatei in Tabellenform aus, wobei
für jeden Record genau eine Zeile ausgegeben wird. Die Spalten
haben dabei folgende Bedeutung:
Alle Angaben sind als hexadezimal zu verstehen.
BIND <Quelldatei(en)> <Zieldatei> [Optionen]
Wie auch AS betrachtet BIND alle nicht mit einem +, - oder /
eingeleiteten Parameter als Dateiangaben, von denen die letzte die
Zieldatei angeben muß. Alle anderen Dateiangaben bezeichnen
Quellen, diese Angaben dürfen auch wieder Jokerzeichen
enthalten.
Um z.B. alle MCS-51-Codeteile aus einer Programmdatei auszusieben,
benutzt man BIND folgendermaßen:
BIND <Quellname> <Zielname> -f $31
Fehlt bei einer Dateiangabe eine Endung, so wird automatisch die
Endung P angefügt.
Wird kein Zielformat explizit angegeben, so wählt P2HEX anhand
des Prozessortyps automatisch eines aus, und zwar S-Records für
Motorola- Prozessoren, Hitachi und TLCS-900(0), MOS für
65xx/MELPS, DSK für die 16-Bit-Texas-Signalprozessoren, Atmel
Generic für die AVRs und Intel-Hex für den Rest. Je nach
Breite der Startadresse kommen bei S-Record Records der Typen 1,2
oder 3 zum Einsatz, jedoch nie in einer Gruppe gemischt. Die Intel-,
Tektronix- und MOS-Formate sind auf 16 Bit-Adressen beschränkt,
das 16-Bit Intel-Format reicht 4 Bit weiter. Längere Adressen
werden von P2HEX mit einer Warnung gemeldet und abgeschnitten(!).
Für die PICs können die drei von Microchip spezifizierten
Varianten des Intel-Hex-Formates erzeugt werden, und zwar mit dem
Schalter
-m <0..3>
Das Format 0 ist INHX8M, in dem alle Bytes in Lo-Hi-Ordnung enthalten
sind. Die Adreßangaben verdoppeln sich, weil bei den PICs die
Adresse sich nur um 1 pro Wort erhöht. Dieses Format ist
gleichzeitig die Vorgabe. Im Format 1 (INHX16M) werden alle Worte in
ihrer natürlichen Ordnung abgelegt. Dieses Format verwendet
Microchip für seine eigenen Programiergeräte. Format 2
(INHX8L) und 3 (INHX8H) trennen die Worte in ihre oberen und unteren
Bytes auf. Um die komplette Information zu erhalten, muß P2HEX
zweimal aufgerufen werden, z.B. so:
p2hex test -m 2
rename test.hex test.obl
p2hex test -m 3
rename test.hex test.obh
Für das Motorola-Format verwendet P2HEX zusätzlich einen in
[6] genannten Recordtyp mit der Nummer 5,
der die Zahl der folgenden Daten-Records (S1/S2/S3) bezeichnet. Da
dieser Typ vielleicht nicht jedem Programm bekannt ist, kann man ihn
mit der Option
+5
unterdrücken.
-r <Startadresse>-<Endadresse>
Die Startadresse ist dabei die erste Speicherzelle, die im Fenster
liegen soll, die Endadresse die der letzten Speicherzelle im
Fenster, nicht die der ersten außerhalb. Um z.B. ein
8051-Programm in 4 2764-EPROMs aufzuteilen, geht man
folgendermaßen vor:
p2hex <Quelldatei> eprom1 -f $31 -r $0000-$1fff
p2hex <Quelldatei> eprom2 -f $31 -r $2000-$3fff
p2hex <Quelldatei> eprom3 -f $31 -r $4000-$5fff
p2hex <Quelldatei> eprom4 -f $31 -r $6000-$7fff
Defaultmäßig ist das Fenster 32 KByte groß und
beginnt bei Adresse 0.
-a
erreichen.
-r $-$
keine Gedanken mehr zu machen. Dollarzeichen und feste Adressen
lassen sich selbstverständlich auch gemischt verwenden, z.B.
kann mit
-r $-$7fff
das obere Ende auf die ersten 32K begrenzt werden.
-d <Start>-<Ende>
festlegen, welche Adreßbereiche als Daten ausgegeben werden
sollen. Dollarzeichen sind hier nicht zugelassen. Für das
DSK- sowie Intel- und Motorola-Format relevant ist dagegen die Option
-e <Adresse> ,
mit der man die in die Hex-Datei einzutragende Startadresse festlegen
kann. Fehlt diese Angabe, so wird nach einen entsprechenden Eintrag
in der Code-Datei gesucht. Ist auch dort kein Hinweis auf einen
Einsprungpunkt zu finden, so wird kein Eintrag in die HEX-Datei
geschrieben (DSK/Intel) bzw. das entsprechende Feld wird auf 0
gesetzt (Motorola).
-l <Anzahl>
tun. Der erlaubte Wertebereich liegt dabei zwischen 2 und 254
Datenbytes; ungerade Werte werden implizit auf gerade Anzahlen
aufgerundet.
-k
kann man P2HEX anweisen, diese automatisch nach der Konversion zu
löschen.
P2HEX <Name>
möglich, um <Name>.HEX aus <Name>.P zu erzeugen.
-S
wird diese Funktion aktiviert. Sie erwartet als Argument eine
Zahlenangabe zwischen 1 und 4, die die Länge des Adressfeldes in
Bytes bestimmt. Optional kann dieser Angabe auch noch der Buchstabe L
oder B vorangestellt werden, um die Byte-Order dieser Adresse
festzulegen. So erzeugt z.B. die Angabe B4 eine
4-Byte-Adresse in Big-Endian-Anordnung, L2 oder nur '2' eine
2-Byte-Adresse in Little-Endian-Anordnung.
Die Option -E sorgt dafür, daß Turbo-Pascal nicht
mit STDOUT und STDERR durcheinander kommt.
A. Fehlermeldungen von AS
Warnung
bei 680x0-,6809- und COP8-Prozessoren: Das Displacement in
einem Adreßausdruck hat den Wert 0 ergeben. Es wird ein
Adreßausdruck ohne Displacement erzeugt. Um keine
Phasenfehler zu erzeugen, werden NOP-Befehle
eingefügt.
keines
Warnung
bei 680x0-, 6502- und 68xx-Prozessoren können bestimmte
Speicherbereiche mit kurzen Adressen erreicht werden. Um
keine Phasefehler zu erzeugen, wird zwar der kürzere
Ausdruck erzeugt, der freie Platz wird aber mit NOPs
aufgefüllt.
keines
Warnung
Bei 680x0 und 8086-Prozessoren kann der Sprung sowohl mit
langem als auch kurzem Displacement ausgeführt werden.
Da kein kurzer Sprung angefordert wurde, wurde im ersten Pass
Platz für den langen Sprung freigehalten. Es wird ein
kurzer Sprung erzeugt, der freie Platz wird mit NOPs
aufgefüllt, um Phasenfehler zu vermeiden.
keines
Warnung
Es wurde eine SHARED-Anweisung gefunden, es wurde
aber keine Kommandozeilenoption angegeben, um eine
Shared-Datei zu erzeugen.
keines
Warnung
Das BCD-Gleitkommaformat der 680x0-Koprozessoren erlaubt zwar
vierstellige Exponenten, lt. Datenbuch können solche
Werte aber nicht korrekt eingelesen werden. Der vierstellige
Wert wird zwar erzeugt, eine Funktion ist aber nicht
gewähleistet.
keines
Warnung
Es wurde eine Anweisung benutzt, die nur im Supervisor-Mode
zulässig ist, obwohl dieser nicht mittels SUPMODE
ON vorher explizit angezeigt wurde.
keines
Warnung
Ein kurzer Sprung mit der Distanz 0 ist bei 680x0- bzw.
COP8-Prozessoren nicht erlaubt, da dieser Sonderwert für
lange Sprünge benötigt wird. Stattdessen wurde ein
NOP-Befehl eingefügt.
keines
Warnung
Das in dem Operanden benutzte Symbol ist aus einem
Adreßraum, der nicht mit dem benutzten Befehl
bearbeitet werden kann.
keines
Warnung
Das in dem Operanden benutzte Symbol ist aus einem
Adreßraum, der mit keinem der Segmentregister des 8086
adressiert werden kann.
Name des nicht adressierbaren Segments
Warnung
Ein Symbol hat einen anderen Wert zugewiesen bekommen als im
vorhergehenden Pass. Diese Warnung wird nur ausgegeben, falls
die r-Option angegeben wurde.
Der Name des fraglichen Symbols
Warnung
Bei der Bildung der Belegungsliste wurde festgestellt,
daß ein Speicherbereich im Codesegment mehrfach benutzt
wurde. Ursache können unüberlegte
ORG-Anweisungen sein.
keines
Warnung
bei einem SWITCH..CASE-Konstrukt ohne
ELSECASE-Zweig traf keiner der CASE-Zweige
zu.
keines
Warnung
Das in dem Operanden benutzte Symbol liegt nicht in der
momentan mit ASSUME eingestellten Fenster
(ST6,78(C)10).
keines
Warnung
Die Hardware erlaubt nur ein Registerpaar zu verketten,
dessen Startadresse gerade ist (RR0, RR2..., nur Z8).
keines
Warnung
Der verwendete Befehl ist zwar noch definiert, ist in seiner
Funktion aber durch andere, neue Befehle ersetzbar und daher
in zukünftigen Prozessorversionen eventuell nicht mehr
vorhanden.
keines
Warnung
Die verwendete Adressierungsart ist bei diesem Befehl zwar
prinzipiell erlaubt, ein Register wird jedoch in einer Weise
doppelt verwendet, daß je nach
Ausührungsreihenfolge sich unterschiedliche Ergebnisse
einstellen können.
keines
Warnung
Ein vorangestellter Klammeraffe dient dazu, sich explizit auf
zu der Sektion lokale Symbole zu beziehen. Wenn man sich
außerhalb einer Sektion befindet, gibt es keine lokalen
Symbole, weshalb dieser Operator überflüssig
ist.
keines
Warnung
Die Anweisung ergibt entweder überhaupt keinen Sinn oder
kann auf andere Weise schneller und kürzer
ausgeführt werden.
keines
Warnung
AS vermutet eine Vorwärtsreferenz eines Symbols, d.h.
das Symbol wird benutzt, bevor es definiert wurde, und
hält einen weiteren Pass für unumgänglich.
Diese Warnung wird nur ausgegeben, falls die
r-Option angegeben wurde.
Der Name des fraglichen Symbols
Warnung
Eine Adresse ist nicht ein mehrfaches der
Operandengröße. Das Datenbuch verbietet zwar
solche Zugriffe, im Instruktionswort ist aber Platz für
diese Adresse, so daß AS es bei einer Warnung belassen
hat.
keines
Warnung
Der verwendete Adressierungsmodus oder die angesprochene
Adresse sind zwar prinzipiell erlaubt, die Adresse liegt aber
im Bereich der Peripherieregister, die in diesem Zusammenhang
nicht verwendet werden dürfen.
keines
Warnung
Ein Register wird in einer Befehlsfolge so verwendet,
daß die Befehlsausführung möglicherweise
nicht in der hingeschriebenen Form ablaufen wird.
Üblicherweise wird ein Register benutzt, bevor der neue
Wert zur Verfügung steht.
das die Verklemmung verursachende Register
Warnung
Ein Adreßregister wird in mehreren
Adreßausdrücken eines Befehls benutzt. Sofern
einer der beiden Ausdrücke das Register modifiziert,
sind die Ergebnisadressen nicht eindeutig festgelegt.
das mehrfach verwendete Register
Warnung
Mit einer SFRB-Anweisung wurde versucht, eine
Speicherstelle als bitadressierbar zu deklarieren, die
aufgrund der Architektur des 8051 nicht bitadressierbar
ist.
keines
Warnung
Am Ende eines Durchlaufes ist ein vom Programm definierter
Stack nicht leer.
der Name des Stacks sowie seine Resttiefe
Warnung
Eine String-Konstante enthält ein NUL-Zeichen. Dies
funktioniert zwar mit der Pascal-Version, in Hinblick auf die
C-Version von AS ist dies aber ein Problem, da C Strings mit
einem NUL-Zeichen terminiert, d.h. der String wäre
für C an dieser Stelle zu Ende...
keines
Warnung
Ein Befehl steht zu Teilen auf verschiedenen Seiten. Da der
Programmzähler des Prozessors aber nicht über
Seitengrenzen hinweg inkrementiert wird, würde zur
Laufzeit anstelle des Instruktionsbytes von der Folgeseite
wieder das erste Byte der alten Seite geholt; das Programm
würde fehlerhaft ablaufen.
keines
Warnung
Ein Zahlenwert lag außerhalb des erlaubten Bereichs. AS
hat den Wert durch ein Abschneiden der oberen Bitstellen in
den erlaubten Bereich gebracht, es ist jedoch nicht
garantiert, daß sich durch diese Operation sinnvoller
und korrekter Code ergibt.
keines
Warnung
Das Wiederholungsargument einer DUP-Direktive war
kleiner als 0. Es werden (analog zu einem Argument von genau
0) keine Daten abgelegt.
keines
Fehler
Einem Symbol wurde durch ein Label oder EQU,
PORT, SFR, LABEL, SFRB
oder BIT ein neuer Wert zugewiesen, dies ist aber
nur bei SET/EVAL erlaubt.
Name des fraglichen Symbols, bei eingeschalteter
Querverweisliste zusätzlich die Zeile der ersten
Definition
Fehler
Ein benutztes Symbol ist auch im 2.Pass noch nicht in der
Symboltabelle enthalten.
Name des nicht gefundenen Symbols
Fehler
Ein Symbolname entspricht nicht den Bedingungen für
einen gültigen Symbolnamen. Beachten Sie, daß
für Makro-und Funktionsparameter strengere Regeln
gelten!
der fehlerhafte Symbolname
Fehler
Das benutzte Befehlsformat existiert bei diesem Befehl
nicht.
Der Kennbuchstabe des verwendeten Formates
Fehler
Der benutzte Befehl (Prozessor oder Pseudo) darf kein mit
einem Punkt angehängtes Attribut haben.
keines
Fehler
Das mit einem Punkt an einen Befehl angehängte Attribut
muß genau ein Zeichen lang sein; weder mehr noch
weniger ist erlaubt.
keines
Fehler
Die bei einem Befehl (Prozessor oder Pseudo) angegebene
Operandenzahl liegt nicht in dem für diesen Befehl
erlaubten Bereich.
keines
Fehler
Die bei diesem Befehl angegebene Zahl von Optionen liegt
nicht in dem für diesen Befehl erlaubten Bereich.
keines
Fehler
Der benutzte Befehl läßt nur immediate-Operanden
(mit vorangestelltem #) zu.
keines
Fehler
Der Operand hat zwar einen für den Befehl zugelassenen
Typ, jedoch nicht die richtige Länge (in Bits).
keines
Fehler
Die angegebenen Operanden haben unterschiedliche Längen
(in Bit).
keines
Fehler
Aus Opcode und Operanden läßt sich die
Operandengröße nicht eindeutig bestimmen (ein
Problem des 8086-Assemblers). Sie müssen die
Operandengröße durch einen BYTE,
WORD, usw. PTR-Präfix festlegen.
keines
Fehler
Ein Ausdruck hat einen an dieser Stelle nicht zulässigen
Typ (Integer/Gleitkomma/String).
Die an dieser Stelle zulässigen Datentypen
Fehler
Einem Befehl wurden mehr als die unter AS zulässigen 20
Parameter übergeben.
keines
Fehler
Der benutzte Befehl ist weder ein Pseudobefehl von AS noch
ein Befehl des momentan eingestellten Prozessors.
keines
Fehler
Der Formelparser ist auf einen (Teil-)Ausdruck
gestoßen, in dem die Summe öffnender und
schließender Klammern nicht übereinstimmt.
der beanstandete (Teil-)Ausdruck
Fehler
Bei einer Division oder Modulooperation ergab die Auswertung
des rechten Teilausdruckes 0.
keines
Fehler
Der angegebene Integer-Wert unterschreitet den
zulässigen Bereich.
aktueller Wert und zulässiges Minimum (manchmal, ich
stelle das gerade um...)
Fehler
Der angegebene Integer-Wert überschreitet den
zulässigen Bereich.
aktueller Wert und zulässiges Maximum (manchmal,ich
stelle das gerade um...)
Fehler
Die angegebene direkte Speicheradresse entspricht nicht den
Ansprüchen des Datentransfers, d.h. ist nicht ein
mehrfaches der Operandengröße. Nicht alle
Prozessoren erlauben unausgerichtete Datenzugriffe.
keines
Fehler
Der in einem Adreßausdruck enthaltene Displacement-Wert
ist zu groß.
keines
Fehler
Die Adresse des Operanden liegt außerhalb des
Speicherbereiches, in dem Kurzadressierung möglich
ist.
keines
Fehler
Der benutzte Adressierungsmodus existiert generell zwar, ist
an dieser Stelle aber nicht erlaubt.
keines
Fehler
An dieser Stelle sind nur ausgerichtete (d.h z.B. gerade)
Adressen erlaubt, da die untersten Bits für andere
Zwecke verwendet werden oder reserviert sind.
keines
Fehler
Die verwendeten Adressierungsmodi sind zwar im sequentiellen
Modus zulässig, jedoch nicht bei parallelen
Instruktionen.
keines
Fehler
Die benutzte Bedingung für bedingte Sprünge
existiert nicht.
keines
Fehler
Sprungbefehl und Sprungziel liegen zu weit auseinander, um
mit einem Sprung der benutzten Länge
überbrückt werden zu können.
keines
Fehler
Da Befehle nur auf geraden Adressen liegen dürfen,
muß eine Sprungdistanz zwischen zwei Befehlen auch
immer gerade sein, das Bit 0 der Distanz wird anderweitig
verwendet. Diese Bedingung ist verletzt worden. Grund ist
üblicherweise die Ablage einer ungeraden Anzahl von
Daten in Bytes oder ein falsches ORG.
keines
Fehler
als Argument für die Schiebeamplitude darf nur eine
Konstante oder ein Datenregister verwendet werden. (nur
680x0)
keines
Fehler
Konstanten für Schiebeamplituden oder
ADDQ-Argumente dürfen nur im Bereich 1..8 liegen.
(nur 680x0)
keines
Fehler
(nicht mehr verwendet)
keines
Fehler
Das Registerlisten-Argument von MOVEM oder
FMOVEM hat ein falsches Format. (nur 680x0)
keines
Fehler
Die verwendete Operandenkombination von CMP ist
nicht erlaubt. (nur 680x0)
keines
Fehler
Den mit CPU angeforderten Zielprozessor kennt AS nicht.
der unbekannte Prozessortyp
Fehler
Das bei z.B. MOVEC benutzte Kontrollregister kennt
der mit CPU gesetzte Prozessor (noch) nicht.
keines
Fehler
Das benutzte Register ist zwar prinzipiell vorhanden, hier
aber nicht erlaubt.
keines
Fehler
Es wurde ein RESTORE-Befehl gefunden, obwohl kein
mit SAVE gespeicherter Zustand (mehr) auf dem Stapel
vorhanden ist.
keines
Fehler
Nach der Assemblierung sind nicht alle SAVE-Befehle
wieder aufgelöst worden.
keines
Fehler
Eine beim MACRO-Befehl zusätzlich angegebene
Steueranweisung ist AS unbekannt.
die fragliche Anweisung
Fehler
Nach der Assemblierung sind nicht alle Konstrukte zur
bedingten Assemblierung aufgelöst worden.
keines
Fehler
Die Reihenfolge der Befehle in einem IF- oder
SWITCH-Konstrukt stimmt nicht.
keines
Fehler
Es existiert bereits eine Sektion gleichen Namens auf dieser
Ebene.
der doppelte Name
Fehler
Im momentanen Sichtbarkeitsbereich existiert keine Sektion
dieses Namens.
der unbekannte Name
Fehler
Nach Ende eines Durchganges sind nicht alle Sektionen wieder
geschlossen worden.
keines
Fehler
die bei ENDSECTION angegebene Sektion ist nicht die
innerste offene.
keines
Fehler
Es wurde ein ENDSECTION-Befehl gegeben, obwohl gar
keine Sektion offen war.
keines
Fehler
ein mit FORWARD oder PUBLIC
angekündigtes Symbol wurde nicht in der Sektion
definiert.
der Name des fraglichen Symbols
Fehler
Ein Symbol wurde sowohl als privat als auch global
definiert.
der Name des Symbols
Fehler
Die Anzahl der Argumente für eine selbstdefinierte
Funktion stimmt nicht mit der geforderten Anzahl
überein.
keines
Fehler
Am Programmende oder beim Umachalten zu einem anderen
Zielprozessor blieben noch nicht abgelegte Literale
übrig.
keines
Fehler
Der benutzte Befehl existiert zwar grundsätzlich, das
eingestellte Mitglied der Prozessorfamilie beherrscht ihn
aber noch nicht.
keines
Fehler
Der benutzte Adressierungsmodus existiert zwar
grundsätzlich, das eingestellte Mitglied der
Prozessorfamilie beherrscht ihn aber noch nicht.
keines
Fehler
Die angegebene Bitnummer ist nicht erlaubt oder eine Angabe
fehlt komplett.
keines
Fehler
Dieser Pseudobefehl darf als Argument nur ON
oder OFF haben.
keines
Fehler
Es wurde bei einem POPV einen Stack anzusprechen,
der entweder nie definiert oder bereits leergeräumt
wurde.
der Name des fraglichen Stacks
Fehler
In einer Bitmaske, die der BITPOS- Funktion
übergeben wurde, war nicht genau ein Bit gesetzt.
keines
Fehler
Eine ENDSTRUCT-Anweisung wurde gegeben, obwohl
momentan keine Strukturdefinition in Gange war.
keines
Fehler
Nach Ende der Assemblierung waren noch nicht alle
STRUCT-Anweisungen durch passende ENDSTRUCTs
abgeschlossen.
die innerste, noch nicht abgeschlossene
Strukturdefinition
Fehler
Der Namensparameter einer ENDSTRUCT-Anweisung
entspricht nicht der innersten, offenen
Strukturdefinition.
keines
Fehler
Was gibt es dazu zu sagen? PHASE in einem Record
ergibt einfach keinen Sinn und nur Verwirrung...
keines
Fehler
Als Direktive für STRUCT ist nur
EXTNAMES oder NOEXTNAMES zugelassen.
die unbekannte Direktive
Fehler
Es wurde mit einem BINCLUDE-Befehl versucht,
über das Ende einer Datei hinauszulesen.
keines
Fehler
Das Konstanten-ROM der 680x0-Koprozessoren hat nur max. 63
Einträge.
keines
Fehler
Als Funktionscodeargument darf nur SFC, DFC, ein
Datenregister oder eine Konstante von 0..15 verwendet werden.
(nur 680x0-MMU)
keines
Fehler
Als Funktionscodemaske darf nur ein Wert von 0..15 verwendet
werden. (nur 680x0-MMU)
keines
Fehler
Die MMU hat kein Register mit dem angegebenen Namen. (nur
680x0-MMU)
keines
Fehler
Die Ebene für PTESTW und PTESTR
muß eine Konstante von 0..7 sein. (nur 680x0-MMU)
keines
Fehler
Die bei den Bit-Feld-Befehlen angegebene Bitmaske hat ein
falsches Format. (nur 680x0)
keines
Fehler
Das angegebene Registerpaar ist hier nicht verwendbar oder
syntaktisch falsch. (nur 680x0)
keines
Fehler
Eine Makrodefinition war am Dateiende nicht zuende.
Vermutlich fehlt ein ENDM.
keines
Fehler
EXITM bricht die Expansion von Makro-Konstrukten ab.
Dieser Befehl macht nur innerhalb von Makros Sinn und es
wurde versucht, ihn außerhalb aufzurufen.
keines
Fehler
Ein Makro darf höchstens 10 Parameter haben.
keines
Fehler
Ein Makronamne wurde in einer Sektion doppelt vergeben.
der doppelt verwendete Name
Fehler
Der benutzte Befehl beeinflußt die Codelänge,
daher sind Vorwärtsreferenzen hier nicht erlaubt.
keines
Fehler
(nicht mehr verwendet)
keines
Fehler
es wurde ein ELSEIF- oder ENDIF-Befehl
gefunden, obwohl kein offener IF-Befehl vorhanden
ist.
keines
Fehler
(nicht mehr verwendet)
keines
Fehler
Die angesprochene Funktion ist weder eingebaut noch
nachträglich definiert worden.
der Funktionsname
Fehler
Das Argument liegt nicht im Bereich der angesprochenen
transzendenten Funktion.
keines
Fehler
Das Argument liegt zwar im Bereich der angesprochenen
transzendenten Funktion, das Ergebnis wäre aber nicht
mehr darstellbar.
keines
Fehler
Das benutzte Pärchen aus Basis und Exponent kann nicht
berechnet werden.
keines
Fehler
Die Prozessorhardware erlaubt keine Sprünge von dieser
Adresse.
keines
Fehler
Die Prozessorhardware erlaubt keine Sprünge zu dieser
Adresse.
keines
Fehler
Sprungbefehl und Sprungziel müssen bei diesem Befehl auf
der gleichen Seite liegen.
keines
Fehler
Es wurde versucht, mehr als 1024 Bytes Code oder Daten in
einer Zeile zu erzeugen.
keines
Fehler
Der Adreßraum dieses Prozessors wurde
überschritten.
keines
Fehler
Anweisungen, die Speicher reservieren und solche, die ihn mit
Konstanten belegen, dürfen nicht in einer
Pseudoanweisung gemischt werden.
keines
Fehler
Ein STRUCT-Konstrukt dient nur der Beschreibung
einer Datenstruktur und nicht dem Anlegen einer solchen, es
sind daher keine Befehle zugelassen, die Code erzeugen.
keines
Fehler
Entweder sind die beiden Instruktionen prinzipiell nicht
parallel ausführbar, oder sie stehen nicht unmittelbar
untereinander.
keines
Fehler
Das angegebene Segment ist an dieser Stelle nicht
anwendbar.
der benutzte Segmentname
Fehler
Das angegebene Segment existiert bei diesem Prozessor
nicht.
der benutzte Segmentname
Fehler
Das angegebene Segmentregister existiert nicht (nur
8086).
keines
Fehler
Der angegebene String hat ein ungültiges Format.
keines
Fehler
Das angegebene Register existiert nicht oder darf hier nicht
verwendet werden.
keines
Fehler
Der angegebene Befehl darf nicht mit einem
REP-Präfix versehen werden.
keines
Fehler
in dieser Kombination ist keine indirekte Adressierung
erlaubt.
keines
Fehler
(nicht mehr verwendet)
keines
Fehler
Dieses Register ist nur im Minimum-Modus definiert.
keines
Fehler
Dieses Register ist nur im Maximum-Modus definiert.
keines
Fehler
Die angegebene Kombination von Präfixen ist nicht
zulässig oder nicht im Maschinenkode darstellbar.
keines
Fehler
Das mit einem Backslash eingeleitete Sonderzeichen ist nicht
definiert.
keines
fatal
Beim Versuch, eine Datei zu öffnen, ist ein Fehler
aufgetreten.
Beschreibung des E/A-Fehlers
fatal
Beim Schreiben des Assemblerlistings ist ein Fehler
aufgetreten.
Beschreibung des E/A-Fehlers
fatal
Beim Lesen aus einer Quelldatei ist ein Fehler
aufgetreten.
Beschreibung des E/A-Fehlers
fatal
Beim Schreiben von Code- oder Share-Datei ist ein Fehler
aufgetreten.
Beschreibung des E/A-Fehlers
fatal
Der verfügbare Speicher reicht nicht mehr, alle
Datenstrukturen aufzunehmen. Weichen Sie auf die DPMI- oder
OS/2-Version von AS aus.
keines
fatal
Der Programmstapel ist wegen zu komplizierter
Formelausdrücke oder einer ungünstigen Anlage der
Symbol- oder Makrotabelle übergelaufen. Versuchen Sie es
noch einmal mit der -A-Option.
keines
Die angegebene Datei existiert nicht oder liegt auf einem anderen
Laufwerk.
Der Pfad eines Dateinamens existiert nicht oder liegt auf einem
anderen Laufwerk.
DOS sind die Dateihandles ausgegangen. Erhöhen Sie die
FILES=-Angabe in der CONFIG.SYS.
Entweder reichen die Netzwerkrechte für einen Dateizugriff
nicht, oder es wurde versucht, eine schreibgeschützte Datei
zu überschreiben oder zu verändern. Bei Benutzung in
DOS- Fenstern von Multitasking- Systemen ist es überdies
möglich, daß ein andere Prozeß die Datei in
exklusivem Zugriff hat.
Das angesprochene Laufwerk existiert nicht.
Eine Datei war zuende, obwohl sie es aufgrund ihrer Struktur noch
nicht sein dürfte. Vermutlich ist sie beschädigt.
Das spricht wohl für sich! Aufräumen!!
Wenn Sie schon keine Festplatte als Arbeitsmedium verwenden, so
sollten Sie wenigstens den Schreibschutz entfernen!
Sie haben versucht, ein Peripheriegerät anzusprechen,
welches DOS unbekannt ist. Dies sollte normalerweise nicht
auftreten, da der Name dann automatisch als Datei interpretiert
wird.
Schließen Sie die Klappe des Diskettenlaufwerks.
Ein harter Lesefehler auf der Diskette. Nochmal versuchen; wenn
immer noch vorhanden, Diskette neu formatieren bzw. ernste Sorgen
um Festplatte machen!
Der Platten/Disketten-Controller hat eine bestimmte Spur nicht
gefunden. Siehe Nr. 154!
DOS kann mit dem Format der Diskette nichts anfangen.
Analog zu Nr. 158, nur daß hier der angeforderte Sektor auf
der Spur nicht gefunden werden konnte.
Offensichtlich haben Sie die Ausgaben von AS direkt auf einen
Drucker umgeleitet. Assemblerlistings können seeehr lang
sein...
Nicht näher vom Gerätetreiber klassifizierter
Lesefehler.
Nicht näher vom Gerätetreiber klassifizierter
Schreibfehler.
Hier ist der Gerätetreiber völlig ratlos, was passiert
sein könnte.
Immer vorhandene Befehle
Zusätzlich existiert SET bzw. EVAL,
falls SET bereits ein Prozessorbefehl ist.
= := ALIGN BINCLUDE CASE CHARSET CPU DEPHASE ELSE ELSECASE ELSEIF END ENDCASE ENDIF ENDM ENDSECTION ENDSTRUCT ENUM ERROR EQU EXITM FATAL FORWARD FUNCTION GLOBAL IF IFB IFDEF IFEXIST IFNB IFNDEF IFNEXIST IFNUSED IFUSED INCLUDE IRP LABEL LISTING MACEXP MACRO MESSAGE NEWPAGE ORG PAGE PHASE POPV PUSHV PRTEXIT PRTINIT PUBLIC READ RELAXED REPT RESTORE SAVE SECTION SEGMENT SHARED STRUCT SWITCH TITLE WARNING WHILE Motorola 680x0
DC[.<size>] DS[.<size>] FULLPMMU FPU PADDING PMMU SUPMODE Motorola 56xxx
DC DS XSFR YSFR PowerPC
BIGENDIAN DB DD DQ DS DT DW SUPMODE Motorola M-Core
DC[.<size>] DS[.<size>] REG SUPMODE Motorola 68xx/Hitachi 6309
ADR BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING RMB Motorola 6805/68HC08
ADR BYT DFS FCB FCC FDB RMB Motorola 6809/Hitachi 6309
ADR ASSUME BYT DFS FCB FCC FDB RMB Motorola 68HC12
ADR BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING RMB Motorola 68HC16
ADR ASSUME BYT DFS FCB FCC FDB RMB Hitachi H8/300(L/H)
DC[.<size>] DS[.<size>] MAXMODE PADDING Hitachi H8/500
ASSUME DC[.<size>] DS[.<size>] MAXMODE PADDING Hitachi SH7x00
COMPLITERALS DC[.<size>] DS[.<size>] LTORG PADDING SUPMODE 65xx/MELPS-740
ADR ASSUME BYT DFS FCB FCC FDB RMB 65816/MELPS-7700
ADR ASSUME BYT DB DD DQ DS DT DW DFS FCB FCC FDB RMB Mitsubishi MELPS-4500
DATA RES SFR Mitsubishi M16
DB DD DQ DS DT DW Mitsubishi M16C
DB DD DQ DS DT DW Intel MCS-48
DB DD DQ DS DT DW Intel MCS-(2)51
BIGENDIAN BIT DB DD DQ DS DT DW PORT SFR SFRB SRCMODE Intel MCS-96
ASSUME DB DD DQ DS DT DW Intel 8080/8085
DATA DS Intel 8080/8085
DB DD DQ DS DT DW PORT Intel i960
DB DD DQ DS DT DW FPU SPACE SUPMODE WORD Signetics 8X30x
LIV RIV Philips XA
ASSUME BIT DB DC[.<size>] DD DQ DS[.<size>] DT DW PADDING PORT SUPMODE AMD 29K
ASSUME DB DD DQ DS DT DW EMULATED SUPMODE Siemens 80C166/167
ASSUME BIT DB DD DQ DS DT DW Zilog Zx80
DB DD DEFB DEFW DQ DS DT DW EXTMODE LWORDMODE Zilog Z8
DB DD DQ DS DT DW SFR Toshiba TLCS-900
DB DD DQ DS DT DW MAXIMUM SUPMODE Toshiba TLCS-90
DB DD DQ DS DT DW Toshiba TLCS-870
DB DD DQ DS DT DW Toshiba TLCS-47(0(A))
ASSUME DB DD DQ DS DT DW PORT Toshiba TLCS-9000
DB DD DQ DS DT DW Microchip PIC16C5x
DATA RES SFR ZERO Microchip PIC16C5x
DATA RES SFR ZERO Microchip PIC17C42
DATA RES SFR ZERO SGS-Thomson ST6
ASCII ASCIZ ASSUME BYTE BLOCK SFR WORD SGS-Thomson ST7
DC[.<size>] DS[.<size>] PADDING SGS-Thomson ST9
ASSUME BIT DB DD DQ DS DT DW REG 6804
ADR BYT DFS FCB FCC FDB RMB SFR Texas TM3201x
DATA PORT RES Texas TM32C02x
BFLOAT BSS BYTE DATA DOUBLE EFLOAT TFLOAT LONG LQxx PORT Qxx RES RSTRING STRING WORD Texas TMS320C3x
ASSUME BSS DATA EXTENDED SINGLE WORD Texas TM32C05x
BFLOAT BSS BYTE DATA DOUBLE EFLOAT TFLOAT LONG LQxx PORT Qxx RES RSTRING STRING WORD Texas TMS9900
BSS BYTE PADDING WORD Texas TMS70Cxx
DB DD DQ DS DT DW Texas TMS370
DB DBIT DD DQ DS DT DW Texas MSP430
BSS BYTE PADDING WORD National SC/MP
DB DD DQ DS DT DW National COP8
ADDR ADDRW BYTE DB DD DQ DS DSB DSW DT FB FW SFR WORD NEC µPD78(C)1x
ASSUME DB DD DQ DS DT DW NEC 75K0
ASSUME BIT DB DD DQ DS DT DW SFR NEC 78K0
DB DD DQ DS DT DW NEC µPD772x
DATA RES NEC µPD772x
DS DW Symbios Logic SYM53C8xx
E. Vordefinierte Symbole
Name
Datentyp
Definition
Bedeutung
ARCHITECTURE
String
vordef.
Zielplattform, für die AS
übersetzt wurde, in der Form
Prozesor-Hersteller-Betriebs-
system
BIGENDIAN
Boolean
normal
Konstantenablage mit MSB
first ?
CASESENSITIVE
Boolean
normal
Unterscheidung von Groß-
und Kleinbuchstaben in
Symbolnamen ?
CONSTPI
Gleitkomma
normal
Kreiszahl Pi (3.1415.....)
DATE
String
vordef.
Datum des Beginns der
Assemblierung (1.Pass)
FALSE
Boolean
vordef.
0 = logisch ,,falsch''
HASFPU
Boolean
dynam.(0)
Koprozessor-Befehle
freigeschaltet ?
HASPMMU
Boolean
dynam.(0)
MMU-Befehle frei-
geschaltet ?
INEXTMODE
Boolean
dynam.(0)
XM-Flag für 4 Gbyte
Adreßraum gesetzt ?
INLWORDMODE
Boolean
dynam.(0)
LW-Flag für 32-Bit-Befehle
gesetzt ?
INMAXMODE
Boolean
dynam.(0)
Prozessor im Maximum-
Modus ?
INSUPMODE
Boolean
dynam.(0)
Prozessor im Supervisor-
Modus ?
INSRCMODE
Boolean
dynam.(0)
Prozessor im Quellmodus ?
FULLPMMU
Boolean
dynam.(0/1)
voller PMMU-Befehlssatz ?
LISTON
Boolean
dynam.(1)
Listing freigeschaltet ?
Name
Datentyp
Definition
Bedeutung
MACEXP
Boolean
dynam.(1)
Expansion von Makrokon-
strukten im Listing
freigeschaltet ?
MOMCPU
Integer
dynam.
(68008)Nummer der momentan
gesetzten Ziel-CPU
MOMCPUNAME
String
dynam.
(68008)Name der momentan
gesetzten Ziel-CPU
MOMFILE
String
Spezial
augenblickliche Quelldatei
(schließt Includes ein)
MOMLINE
Integer
Spezial
aktuelle Zeilennummer in
der Quelldatei
MOMPASS
Integer
Spezial
Nummer des laufenden
Durchgangs
MOMSECTION
String
Spezial
Name der aktuellen Sektion
oder Leerstring, falls außerhalb
aller Sektionen
MOMSEGMENT
String
Spezial
Name des mit SEGMENT einge-
stellten Adreßraumes
PADDING
Boolean
dynam.(1)
Auffüllen von Bytefeldern
auf ganze Anzahl ?
RELAXED
Boolean
dynam.(0)
Schreibweise von Integer-Kon-
stanten in beliebiger Syntax
erlaubt ?
PC
Integer
Spezial
mom. Programmzähler
(Thomson)
TIME
String
vordef.
Zeit des Beginns der Assem-
blierung (1. Pass)
TRUE
Integer
vordef.
1 = logisch ,,wahr''
VERSION
Integer
vordef.
Version von AS in BCD-Kodie-,
rung, z.B. 1331 hex für
Version 1.33p1
*
Integer
Spezial
mom. Programmzähler (Moto-
rola, Rockwell, Microchip,
Hitachi)
$
Integer
Spezial
mom. Programmzähler (Intel,
Zilog, Texas, Toshiba, NEC,
Siemens, AMD)
''If I have seen farther than other men,
it is because I stood on the shoulders of giants.''
--Sir Isaac Newton
''If I haven't seen farther than other men,
it is because I stood in the footsteps of giants.''
--unknown
H. Änderungen seit Version 1.3
I. Hinweise zum Quellcode von AS
All das führte dazu, daß ich Turbo-Pascal zur
Implementierung von AS benutzte, und bis heute ist das so geblieben.
Die Zeiten haben sich aber geändert: Mit der Einstellung der
Turbo/Borland-Pascal-Linie durch Borland gibt es an aktuellen
Pascal-Compilern nur noch so etwas wie Delphi, das m.E. eigentlich
nur zur Entwicklung von Programmen taugt, die aus 90% Oberfläche
bestehen, für ein kommandozeilengesteuertes Programm wie AS also
völlig ungeeignet ist. Außerdem hat sich mein
betriebssystemmäßiger Schwerpunkt inzwischen deutlich in
Richtung Unix verschoben, und der ANSI-Standard hat sich inzwischen
durchgesetzt. Eine Portierung der AS-Sourcen auf C habe ich daher
inzwischen fast beendet, momentan ist die Pascal-Version aber noch
die ,,Referenz''. Zum Übersetzen wird Borland-Pascal in der
Version 7 empfohlen; mit Version 6 geht es zur Not zwar auch, man
kann einige Features (wie Protected Mode) aber nicht ausnutzen.
; Longint shift right
; In DX:AX = Value
; CX = Shift count
; Out DX:AX = Result
; Correction 11.6.1994 AA
LongShr:
CMP Test8086,2
JB @@1
.386
SHL EAX,16
SHRD EAX,EDX,16
SHR EAX,CL
SHLD EDX,EAX,16
RETF
.8086
@@1: AND CX,1FH
JE @@3
@@2: SHR DX,1
RCR AX,1
LOOP @@2
@@3: RETF
; Longint shift left
; In DX:AX = Value
; CX = Shift count
; Out DX:AX = Result
; Correction 11.6.1994 AA
LongShl:
CMP Test8086,2
JB @@1
.386
SHL EAX,16
SHRD EAX,EDX,16
SHL EAX,CL
SHLD EDX,EAX,16
RETF
.8086
@@1: AND CX,1FH
JE @@3
@@2: SHL AX,1
RCL DX,1
LOOP @@2
@@3: RETF
Wer keine Quellen hat, kann alternativ auch als ,,Notlösung''
die Variable Test8086 im Hauptprogramm auf einen Wert <2
setzen und so die Optimierungen ganz unterbinden...
STDINC.PAS
AS.PAS
ASMDEF.PAS
ASMSUB.PAS
ASMPARS.PAS
ASMMAC.PAS
ASMIF.PAS
ASMCODE.PAS
CODEALLG.PAS
CODEPSEU.PAS
DECODECM.PAS
Dieses Modul wird nicht nur von AS, sondern auch von den
Hilfsprogrammen BIND, P2HEX und P2BIN verwendet.
STDHANDL.PAS
über normale Text-Variablen zuzugreifen. AS benutzt momentan nur
STDERR.
NLS.PAS
AS benutzt noch nicht alle von dieser Unit angebotenen Informationen
(was will man auch dort mit Geldbeträgen? :-) ), die
NLS-Unterstützung wird in Zukunft aber noch besser werden.
Insbesondere diese Unit bietet sich zur Verwendung in anderen
Programmen an: Durch schlichtes Einbinden erhält man eine
korrekt für alle Zeichen arbeitende UpCase-Funktion,
die Borland uns schon seit Jahren vorenthält!
STRINGLI.PAS
STRINGUT.PAS
CHUNKS.PAS
INCLIST.PAS
FILENUMS.PAS
CODExxxx.PAS
I.3. Neuer Prozessor...was nun ?
Festlegung des Prozessornamens
Definition des Codegeneratormoduls
CPUxxxx:=AddCPU('XXXX',SwitchTo_xxxx);
'XXXX' ist dabei der für den Prozessor festgelegte
Name, der später im Assemblerprogramm verwendet werden
muß, um AS auf diesen Zielprozessor umzuschalten.
SwitchTo_xxxx (im folgenden kurz als ,,Umschalter'' bezeichnet)
ist eine parameterlose Prozedur, die von AS aufgerufen wird, sobald
auf diesen Prozessor umgeschaltet werden soll. Als Ergebnis
liefert AddCPU eine Zahlenwert, der als interne ,,Kennung''
für diesen Prozessor fungiert. In der globalen Variablen
MomCPU wird ständig die Kennung des momentan gesetzten
Zielprozessors mitgeführt. Der von AddCPU gelieferte
Wert sollte in einer privaten Variable des Typs CPUVar
(hier CPUxxxx genannt) abgelegt werden. Falls ein
Codegeneratormodul verschiedene Prozessoren (z.B. einer Familie)
verwaltet, kann es so durch Vergleich von MomCPU gegen diese
Werte feststellen, welche Befehlsuntermenge momentan zugelassen ist.
Gehen Sie nicht davon aus, daß eine dieser Variablen einen
vordefinierten Wert hat, sondern besetzen Sie ALLE Felder
neu!!
Die Routinen, auf die diese drei Prozedurvariablen zeigen, sind alle
parameterlos und müssen (wie der Umschalter übrigens auch)
als FAR deklariert werden, sonst können sie nicht einer
Prozedurvariable zugewiesen werden.
AddCopyright("Intel 80986-Codegenerator (C) 2010 Hubert Simpel");
Der übergebene String wird dann nach dem Programmstart
zusätzlich zu der Standardmeldung ausgegeben.
Eintragen des Codegeneratormodules
Schreiben des eigentlichen Codegenerators
Mit Sicherheit wird auch das Studium der vorhandenen Module
weiterhelfen.
Modifikation der Dienstprogramme
68030 Assembly Language Reference.
Addison-Wesley, Reading, Massachusetts, 1989
AM29240, AM29245, and AM29243 RISC
Microcontrollers.
1993
AVR Enhanced RISC Microcontroller Data Book.
May 1996
8-Bit AVR Assembler and Simulator Object File Formats
(Preliminary).
(Teil der AVR-Tools-Dokumentation)
G65SC802 / G65SC816 CMOS 8/16-Bit Microprocessor Family
Data Sheet.
CP/M 68K Operating System User's Guide.
1983
FasMath 83D87 User's Manual.
1990
DS80C320 High-Speed Micro User's Guide.
Version 1.30, 1/94
8-/16-Bit Microprocessor Data Book.
1986
Understanding HD6301X/03X CMOS Microprocessor
Systems.
erschienen bei Hitachi
H8/300H Series Programming Manual.
(21-032, keine Jahresangabe)
SH Microcomputer Hardware Manual (Preliminary).
SH7700 Series Programming Manual.
1st Edition, September 1995
H8/500 Series Programming Manual.
(21-20, 1st Edition Feb. 1989)
H8/532 Hardware Manual.
(21-30, keine Jahresangabe)
H8/534, H8/536 Hardware Manual.
(21-19A, keine Jahresangabe)
PPC403GA Embedded Controller User's Manual.
First Edition, September 1994
1987
Microprocessor and Peripheral Handbook.
Volume I Microprocessor, 1988
80960SA/SB Reference Manual.
1991
8XC196NT Microcontroller User's Manual.
June 1995
8XC251SB High Performance CHMOS Single-Chip
Microcontroller.
Sept. 1995, Order Number 272616-003
80296SA Microcontroller User's Manual.
Sept. 1996
A memo on the secret features of 6309.
(erhältlich über WWW:
http://www.cs.umd.edu/users/fms/comp/CPUs/6309.txt)
Microchip Data Book.
1993 Edition
Single-Chip 8-Bit Microcomputers
Vol.2, 1987
Single-Chip 16-Bit Microcomputers.
Enlarged edition, 1991
Single-Chip 8 Bit Microcomputers.
Vol.2, 1992
M34550Mx-XXXFP Users's Manual.
Jan. 1994
M16 Family Software Manual.
First Edition, Sept. 1994
M16C Software Manual.
First Edition, Rev. C, 1996
M30600-XXXFP Data Sheet.
First Edition, April 1996
Microprocessor, Microcontroller and Peripheral
Data.
Vol. I+II, 1988
MC68881/882 Floating Point Coprocessor User's
Manual.
Prentice-Hall, Englewood Cliffs, Second Edition 1989
MC68851 Paged Memory Management Unit User's
Manual.
Prentice-Hall, Englewood Cliffs, Second Edition 1989
CPU32 Reference Manual.
Rev. 1, 1990
DSP56000/DSP56001 Digital Signal Processor User's
Manual.
Rev. 2, 1990
MC68340 Technical Summary.
Rev. 2, 1991
CPU16 Reference Manual.
Rev. 1, 1991
Motorola M68000 Family Programmer's Reference
Manual.
1992
MC68332 Technical Summary.
Rev. 2, 1993
PowerPC 601 RISC Microprocessor User's Manual.
1993
PowerPC(tm) MPC505 RISC Microcontroller Technical
Summary.
1994
CPU12 Reference Manual.
1st. edition, 1996
CPU08 Reference Manual.
Rev. 1 (keine Jahresangabe im PDF-File)
MC68360 User's Manual.
MCF 5200 ColdFire Family Programmer's Reference
Manual.
1995
M*Core Programmer's Reference Manual.
1997
DSP56300 24-Bit Digital Signal Processor Family
Manual.
Rev. 0 (keine Jahresangabe im PDF-File)
SC/MP Programmier- und Assembler-Handbuch.
Publication Number 4200094A, Aug. 1976
COP800 Assembler/Linker/Librarian User's Manual.
Customer Order Number COP8-ASMLNK-MAN, NSC Publication Number
424421632-001B, August 1993
COP87L84BC microCMOS One-Time-Programmable (OTP)
Microcontroller.
Preliminary, March 1996
µpD70108 / µpD70116 / µpD70208 /
µpD70216 / µpD72091 Data Book.
(keine Jahresangabe)
User's Manual µCOM-87 AD Family.
(keine Jahresangabe)
µCOM-75x Family 4-bit CMOS Microcomputer User's
Manual.
Vol. I+II (keine Jahresangabe)
Digital Signal Processor Product Description.
PDDSP.....067V20 (keine Jahresangabe)
µPD78070A, 78070AY 8-Bit Single-Chip Microcontroller
User's Manual.
Document No. U10200EJ1V0UM00 (1st edition), August 1995
Data Sheet µPD78014.
16-bit 80C51XA Microcontrollers (eXtended
Architecture).
Data Handbook IC25, 1996
8 Bit MCU Families EF6801/04/05 Databook.
1st edition, 1989
ST6210/ST6215/ST6220/ST6225 Databook.
1st edition, 1991
ST7 Family Programming Manual.
June 1995
ST9 Programming Manual.
3rd edition, 1993
SAB80C166/83C166 User's Manual.
Edition 6.90
SAB C167 Preliminary User's Manual.
Revision 1.0, July 1992
SAB-C502 8-Bit Single-Chip Microcontroller User's
Manual.
Edition 8.94
SAB-C501 8-Bit Single-Chip Microcontroller User's
Manual.
Edition 2.96
C504 8-Bit CMOS Microcontroller User's Manual.
Edition 5.96
Programmierung des 68000.
Sybex-Verlag Düsseldorf, 1985
Symbios Logic PCI-SCSI-I/O Processors PRogramming
Guide.
Version 2.0, 1995/96
Model 990 Computer/TMS9900 Microprocessor Assembly Language
Programmer's Guide.
1977, Manual No. 943441-9701
TMS9995 16-Bit Microcomputer Preliminary Data
Manual.
1981
First-Generation TMS320 User's Guide.
1988, ISBN 2-86886-024-9
TMS7000 family Data Manual.
1991, DB103
TMS320C3x User's Guide.
Revision E, 1991
TMS320C2x User's Guide.
Revision C, Jan. 1993
TMS370 Family Data Manual.
1994, SPNS014B
MSP430 Family Software User's Guide.
1994, SLAUE11
MSP430 Metering Application.
1996, SLAAE10A
MSP430 Family Architecture User's Guide.
1995, SLAUE10A
TMS320C62xx CPU and Instruction Set Reference
Manual.
Jan. 1997, SPRU189A
8-Bit Microcontroller TLCS-90 Development System
Manual.
1990
8-Bit Microcontroller TLCS-870 Series Data Book.
1992
16-Bit Microcontroller TLCS-900 Series Users
Manual.
1992
16-Bit Microcontroller TLCS-900 Series Data Book:
TMP93CM40F/TMP93CM41F.
1993
4-Bit Microcontroller TLCS-47E/47/470/470A Development
System Manual.
1993
TLCS-9000/16 Instruction Set Manual Version 2.2.
10. Feb 1994
Bipolare Mikroprozessoren und bipolare
LSI-Schaltungen.
Datenbuch, 1985, ISBN 3-87095-186-9
Z8 Microcontrollers Databook.
1992
Discrete Z8 Microcontrollers Databook.
(keine Jahresangabe)
Z380 CPU Central Processing Unit User's Manual.
(keine Jahresangabe)
ADDR 1 | ADDRW 1 | ADR 1 | ALIGN 1 | ASCII 1 |
ASCIZ 1 | ASSUME 1 | BFLOAT 1 | BIGENDIAN 1 | BINCLUDE 1 |
BIT 1 | BLOCK 1 | BRANCHEXT 1 | BSS 1 | BYT 1 |
BYTE 1 2 | CASE 1 | CHARSET 1 | CODEPAGE 1 | CPU 1 |
DATA 1 | DB 1 | DBIT 1 | DC 1 | DD 1 |
DEFB 1 | DEFW 1 | DEPHASE 1 | DFS 1 | DOUBLE 1 |
DQ 1 | DS 1 2 | DSB 1 | DSW 1 | DT 1 |
DUP 1 | DW 1 | EFLOAT 1 | ELSE 1 | ELSECASE 1 |
ELSEIF 1 | EMULATED 1 | END 1 | ENDCASE 1 | ENDIF 1 |
ENDM 1 | ENDSTRUCT 1 | ENUM 1 | EQU 1 | ERROR 1 |
EXITM 1 | EXTENDED 1 | EXTMODE 1 | FATAL 1 | FB 1 |
FCB 1 | FCC 1 | FDB 1 | FLOAT 1 | FPU 1 |
FULLPMMU 1 | FUNCTION 1 | FW 1 | IF 1 | IFB 1 |
IFDEF 1 | IFEXIST 1 | IFNB 1 | IFNDEF 1 | IFNEXIST 1 |
IFNUSED 1 | IFUSED 1 | INCLUDE 1 | IRP 1 | IRPC 1 |
LABEL 1 | LISTING 1 | LIV 1 | LONG 1 | LQxx 1 |
LTORG 1 | LWORDMODE 1 | MACEXP 1 | MACRO 1 | MAXMODE 1 |
MESSAGE 1 | NEWPAGE 1 | ORG 1 | PADDING 1 | PAGE 1 |
PHASE 1 | PMMU 1 | POPV 1 | PORT 1 | PRTEXIT 1 |
PRTINIT 1 | PUSHV 1 | Qxx 1 | RADIX 1 | READ 1 |
REG 1 | RELAXED 1 | REPT 1 | RES 1 | RESTORE 1 |
RIV 1 | RMB 1 | RSTRING 1 | Registersymbole 1 | SAVE 1 |
SEGMENT 1 | SET 1 | SFR 1 | SFRB 1 | SHARED 1 2 3 |
SINGLE 1 | SPACE 1 | SRCMODE 1 | STRING 1 | STRUCT 1 |
SUPMODE 1 | SWITCH 1 | TFLOAT 1 | TITLE 1 | WARNING 1 |
WHILE 1 | WORD 1 2 | XSFR 1 | YSFR 1 | ZERO 1 |