Makroassembler AS V1.42
Benutzeranleitung
Stand Juni 2017
IBM, PPC403Gx, 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. .
PicoBlaze ist ein eingetragenes Warenzeichen der Xilinx Inc.
UNIX ist ein eingetragenes Warenzeichen der Open Group.
Linux ist ein eingetragenes Warenzeichen von Linus Thorvalds.
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 dem Betriebssystem Linux 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 untersteht der Gnu General Public License (GPL); die Details dieser Lizenz können Sie in der beiliegenden Datei COPYING nachlesen. Falls Sie diese nicht mit AS erhalten haben, beschweren Sie sich bei demjenigen, von dem Sie AS erhalten haben!
Kurz gesagt, beinhaltet die GPL folgende Punkte:
Um eine möglichst schnelle Fehlerdiagnose und -korrektur zu ermöglichen, bitte ich, Fehlerberichten 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, C) findet sich auf folgendem Server:
http://john.ccac.rwth-aachen.de:8000/asoder auch kurz
http://www.alfsembler.de
Wer über keinen FTP-Zugang verfügt, kann den Assembler auch von mir anfordern. Ich werde aber nur Anfragen beantworten, die einen CD-Rohling 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.
Wer einen Blick in die Quellen von AS werfen will, besorge sich einfach die Unix-Version von AS, die als Quelltext zum Selberübersetzen kommt. Die Quellen sind mit Sicherheit nicht in einem Format, daß das Verständnis möglichst leicht macht - an vielen Stellen schaut noch der originale Pascal-Quellcode heraus, und ich teile einige häufig vertretene Ansichten über 'guten' C-Stil nicht...
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 unter DOS 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. Seit 1.41r8 ist diese nur eine volle 32-bittige OS/2-Anwendung, was natürlich zur Folge hat, daß OS/2 2.x und ein 80386-Prozessor jetzt zwingend erforderlich sind.
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...).
Scotty: Captain, we din' can reference it!
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.
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.
Prinzipiell erhält man AS in einer von zwei Formen: Als Binärdistribution oder Quellcodedistribution. Im Falle einer Binärdistribution bekommt man AS mit den zugehörigen Dienstprogrammen und Hilfsdateien fertig übersetzt, so daß man nach dem Auspacken des Archivs an die gewünschte Stelle direkt loslegen kann. Binärdistributionen werden für verbreitete Plattformen gemacht, bei denen die Mehrzahl der Benutzer keinen Compiler hat oder die Übersetzung trickreich ist (im Moment sind dies DOS und OS/2). Eine Quellcodedistribution enthält im Gegensatz den kompletten Satz an C-Quellen, um AS zu generieren; es ist letzten Endes ein Schnappschuß des Quellenbaumes, an dem ich AS weiterentwickele. Die Generierung von AS aus dem Quellcode und dessen Struktur ist näher in Anhang I beschrieben, weshalb an dieser Stelle nur auf den Umfang und die Installation einer Binärdistribution beschrieben wird:
Das Archiv des Lieferumfangs gliedert sich in einige Unterverzeichnisse, so daß man nach dem Auspacken sofort einen Verzeichnisbaum erhält. Die Verzeichnisse enthalten im einzelnen:
Datei | Funktion |
---|---|
Verzeichnis BIN | |
AS.EXE PLIST.EXE PBIND.EXE P2HEX.EXE P2BIN.EXE AS.MSG PLIST.MSG PBIND.MSG P2HEX.MSG P2BIN.MSG TOOLS.MSG CMDARG.MSG IOERRS.MSG |
Programmdatei Assembler listet Inhalt von Codedateien auf kopiert Codedateien zusammen wandelt Code- in Hexdateien um wandelt Code- in Binärdateien um Textresourcen zu AS Textresourcen zu PLIST Textresourcen zu PBIND Textresourcen zu P2HEX Textresourcen zu P2BIN gemeinsame Textresourcen zu den Tools gemeinsame Textresourcen zu allen Programmen |
Verzeichnis DOC | |
AS_DE.DOC AS_DE.HTML AS_DE.TEX AS_EN.DOC AS_EN.HTML AS_EN.TEX |
deutsche Dokumentation, ASCII-Format deutsche Dokumentation, HTML-Format deutsche Dokumentation, LaTeX-Format englische Dokumentation, ASCII-Format englische Dokumentation, HTML-Format englische Dokumentation, LaTeX-Format |
Verzeichnis INCLUDE | |
BITFUNCS.INC CTYPE.INC 80C50X.INC 80C552.INC H8_3048.INC REG166.INC REG251.INC REG29K.INC REG53X.INC REG6303.INC REG683XX.INC REG7000.INC REG78310.INC REG78K0.INC |
Funktionen zur Bitmanipulation Funktionen zur Klassifizierung von Zeichen Registeradressen SAB C50x Registeradressen 80C552 Registeradressen H8/3048 Adressen & Befehlsmakros 80C166/167 Adressen & Bits 80C251 Peripherieadressen AMD 2924x Registeradressen H8/53x Registeradressen 6303 Registeradressen 68332/68340/68360 Registeradressen TMS70Cxx Registeradressen & Vektoren 78K3 Registeradressen 78K0 |
Datei | Funktion |
---|---|
Verzeichnis INCLUDE | |
REG96.INC REGACE.INC REGAVR.INC REGCOP8.INC REGGP32.INC REGHC12.INC REGM16C.INC REGMSP.INC REGST9.INC REGZ380.INC STDDEF04.INC STDDEF16.INC STDDEF17.INC STDDEF18.INC STDDEF2X.INC STDDEF37.INC STDDEF3X.INC STDDEF4X.INC STDDEF47.INC STDDEF51.INC STDDEF56K.INC STDDEF5X.INC STDDEF60.INC STDDEF62.INC STDDEF75.INC STDDEF87.INC STDDEF90.INC STDDEF96.INC STDDEFXA.INC STDDEFZ8.INC |
Registeradressen MCS-96 Registeradressen ACE Register- & Bitadressen AVR-Familie Registeradressen COP8 Registeradressen 68HC908GP32 Registeradressen 68HC12 Registeradressen Mitsubishi M16C Registeradressen TI MSP430 Register- & Makrodefinitionen ST9 Registeradressen Z380 Registeradressen 6804 Befehlsmakros und Registeradressen PIC16C5x Registeradressen PIC17C4x Registeradressen PIC16C8x Registeradressen TMS3202x Register- & Bitadressen TMS370xxx Peripherieadressen TMS320C3x Peripherieadressen TMS320C4x 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 |
Verzeichnis LIB |
Datei | Funktion |
---|---|
Verzeichnis MAN | |
ASL.1 PLIST.1 PBIND.1 P2HEX.1 P2BIN.1 |
Kurzanleitung zu AS Kurzanleitung zu PLIST Kurzanleitung zu PBIND Kurzanleitung zu P2HEX Kurzanleitung zu P2BIN |
Je nach Plattform kann eine Binärdistribution aber noch weitere Dateien enthalten, um einen Betrieb zu ermöglichen, wie es z.B. bei DOS-Extendern der Fall ist. Für die DOS-DPMI-Version ergeben sich die in Tabelle 2.4 gelisteten Ergänzungen. Es spricht übrigens nichts dagegen, als Hilfsprogramme die Versionen aus einer DOS-Distribution zu verwenden, da diese einerseits ohne den Extender-Overhead deutlich schneller ablaufen und andererseits den vom Extender bereitgestellten erweiterten Speicher nicht benötigen.
Datei | Funktion |
---|---|
Verzeichnis BIN | |
DPMI16BI.OVL RTM.EXE |
DPMI-Server für den Assembler Laufzeit-Modul des Extenders |
Eine OS/2-Binärdistribution enthält neben den Basisdateien eine Reihe von DLLs, die zur Laufzeitumgebung des verwendeten emx-Compilers gehören (Tabelle 2.5). Falls man diese DLLs (oder neuere Versionen davon) bereits besitzt, kann man diese auch wieder löschen und seine eigenen benutzen.
Datei | Funktion |
---|---|
Verzeichnis BIN | |
EMX.DLL EMXIO.DLL EMXLIBC.DLL EMXWRAP.DLL |
Laufzeitbibliotheken für AS und die Dienstprogramme |
Eine besondere Installation ist für die Nutzung einer Binärdistribution nicht notwendig, es genügt, das Archiv an passender Stelle auszupacken und dann noch einige Kleinigkeiten zu ergänzen. Als Beispiel hier eine Installation, wie sie vielleicht ein UNIX-Anhänger vornehmen würde:
Legen Sie ein Verzeichnis c:\as an (im folgenden nehme ich an, daß Sie AS auf Laufwerk C installieren wollen), wechseln Sie in dieses und entpacken Sie das Archiv unter Erhalt der Verzeichnisnamen (bei Verwendung von PKUNZIP ist dazu die Kommandozeilenoption -d erforderlich). Sie sollten jetzt folgenden Verzeichnisbaum haben:
c:\as c:\as\bin c:\as\include c:\as\lib c:\as\man c:\as\docErgänzen Sie jetzt die PATH-Anweisung in Ihrer AUTOEXEC.BAT um das Verzeichnis c:\as\bin, so daß AS und seine Hilfsprogramme vom System gefunden werden. In dem lib-Verzeichnis erzeugen Sie mit einem beliebigen Texteditor eine Datei AS.RC mit folgendem Inhalt:
-i c:\as\includeDiese 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.rcWas Sie alles noch in der Key-Datei voreinstellen können, steht im folgenden Abschnitt.
Die Installation der DPMI-Version sollte im Prinzip genauso verlaufen wie der reinen DOS-Version; wenn der Pfad das bin-Verzeichnis enthält, werden die Dateien des DOS-Extenders automatisch gefunden und man sollte von dieser Mimik (mit Ausnahme der längeren Anlaufzeit...) nichts mitbekommen. Theoretisch ist es möglich, daß Sie auf 80286-Rechnern beim ersten Start mit einer Meldung der folgenden Form konfrontiert werden:
machine not in database (run DPMIINST)Da das Tool DPMIINST bei neueren Versionen des DOS-Extenders von Borland aber nicht mehr dabei ist, nehme ich einmal an, daß diese Sache sich erledigt hat...falls doch nicht, bitte ich um Rückmeldung!
Die Installation der OS/2-Version kann in weiten Zügen genauso ablaufen wie für die DOS-Version, nur daß dem System noch die DLLs bwkannt gemacht werden müssen. Wenn Sie den LIBPATH-Eintrag in Ihrer CONFIG.SYS nicht erweitern wollen, ist es natürlich auch möglich, die DLLs in ein Verzeichnis zu verschieben, das bereits dort aufgeführt ist.
Wie bereits erwähnt, beschränkt sich die Installationsbeschreibung hier nur auf Binärdistributionen. Da eine Installation unter Unix im Augenblick immer eine Quellcodedistribution ist, geht der Verweis hier unisono in Anhang I.
AS ist ein kommandozeilengesteuertes Programm, d.h. alle Parameter und Dateiangaben sind in der Kommandozeile anzugeben.
Zu AS gehört eine Reihe Reihe von Nachrichtendateien (erkennbar an der Endung MSG, aus denen AS zur Laufzeit die für die jeweilige Landessprache dynamisch nachlädt. AS sucht nach diesen Dateien in den folgenden Verzeichnissen:
Die Auswahl der Sprache (momentan Deutsch oder Englisch) orientiert sich unter DOS und OS/2 an der COUNTRY-Einstellung in der CONFIG.SYS, unter Unix an der LANG-Environment-Variablen.
Um den Speicherbedarf von AS unter DOS überhaupt befriedigen zu können, wurden die verschiedenen Codegeneratormodule in der DOS-Version in einen Overlay verlegt, der Teil des EXE-Files ist. Eine getrennte OVR-Datei wie bei früheren Versionen von AS existiert also nicht mehr, AS versucht aber wie bisher auch weiterhin, die durch das Overlaying entstehenden Verzögerungen durch Nutzung von eventuellem EMS- oder XMS-Speicher zu reduzieren. Sollte diese Verwendung 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
SET USEXMS=ndie Verwendung von extended memory verhindern.
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:
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.
Die Kommandozeilenparameter können grob in drei Klassen eingeteilt werden: Schalter, Key-File-Referenzen (s.u.) 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:
-queitanstelle von
-quietgeschrieben 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.
Momentan sind folgende Schalter definiert:
as test*.asm firstprog -cl /i c:\as\8051\includeEs 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.
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:
as -g test.asmDie Lösung wäre in diesem Fall, die -g-Option ans Ende der Kommandozeile zu setzen oder ein explizites MAP-Argument zu spezifizieren.
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
set ASCMD=-L -i c:\as\8051\includeeine Menge Tipparbeit ersparen. Da die Environment-Optionen vor der Kommandozeile abgearbeitet werden, können Optionen in der Kommandozeile widersprechende im Environment übersteuern.
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.
set ASCMD=@c:\as\as.keyUm 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.
Referenzen auf eine Key-Datei können nicht nur von der ASCMD-Variablen aus erfolgen, sondern auch direkt von der Kommandozeile aus, indem man analog zur ASCMD-Variablen dem Dateinamen einen Klammeraffen voranstellt:
as @<Datei> ....Die in einem solchen Fall aus dem Key-File gelesenen Optionen werden so eingearbeitet, als hätten sie anstelle dieser Referenz in der Kommandozeile gestanden - es ist also nicht wie bei der ASCMD-Variablen so, daß sie vor allen anderen Kommandozeilenoptionen abgearbeitet werden würden.
Das Referenzieren eines Key-Files von einem Key-File selber ist nicht erlaubt und wird von AS mit einer Fehlermeldung quittiert.
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:
/~I --> /i
-#u --> -U
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
511 KByte verfügbarer Restspeicherbedeutet 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...
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:
[Label[:]]<Befehl>[.Attribut] [Parameter[,Parameter..]] [;Kommentar]Eine Zeile darf dabei auch über mehrere Zeilen in der Quelldatei verteilt sein, Folgezeichen (\) verketten diese Teile dann zu einer einzigen Zeile. Zu beachten ist allerdings, daß aufgrund der internen Pufferstruktur die Gesamtzeile nicht 256 Zeichen überschreiten darf. Zeilenangaben in Fehlermeldungen beziehen sich immer auf die letzte Zeile einer solchen zusammengesetzten Zeile.
Der Doppelpunkt nach dem Label ist optional, falls das Label in der ersten Spalte beginnt (woraus folgt, daß ein Befehl, sei es ein Maschinen- oder Pseudobefehl 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. Diese Einschränkung ist aus Eindeutigkeitsgründen nötig, da sonst keine Unterscheidung zwischen Befehl mit Format und Label mit Befehl möglich wäre.
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/C4x), 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.6).
Attribut | arithmetisch-logischer Befehl | Sprungbefehl |
---|---|---|
B W L Q S D X P |
Byte (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 --------- |
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:
add.w:g rw10,rw8Was 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. [120], [27], [49] bzw. [50] entnommen werden.
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:
[<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.
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.7 aufgelistet.
Bit | Teil |
---|---|
0 1 2 3 4 5 7 |
Quelldatei(en)+erzeugter Code Symboltabelle Makroliste Funktionsliste Zeilennumerierung Registersymboltabelle Zeichentabellenliste |
Defaultmäßig sind alle Bits auf 1 gesetzt, bei Verwendung des Schalters
-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.
In der Querverweisliste wird für jedes definierte Symbol in alphabetischer Reihenfolge eine Ausgabe folgender Form erzeugt:
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.
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.8 zeigt die wichtigsten, von AS vordefinierten Symbole.
Name | Bedeutung |
---|---|
TRUE FALSE CONSTPI VERSION ARCHITECTURE DATE TIME MOMCPU MOMCPUNAME MOMFILE MOMLINE MOMPASS MOMSECTION MOMSEGMENT *, $ bzw. PC |
logisch ,,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 |
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:
cnt set cnt+1 temp equ "\{CNT}" jnz skip{temp} . . skip{temp}: nopACHTUNG! Der Programmierer ist selber dafür verantwortlich, daß sich dabei gültige Symbolnamen ergeben!
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:
Label: . . Attr equ symtype(Label) ; ergibt 1Den einzelnen Segmenttypen sind die in Tabelle 2.9 aufgelisteten Nummern zugeordnet. Die aus der Ordnung normaler Symbole etwas herausfallenden Registersymbole sind näher in Abschnitt 2.11 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 128 |
Besonders bei Programmen mit vielen aufeinanderfolgenden Schleifen oder IF-artigen Strukturen steht man immer wieder vor dem Problem, sich ständig neue Namen für Labels ausdenken zu müssen. Dabei weiß man an sich genau, daß man dieses Label nie wieder brauchen wird und am liebsten in irgendeiner Weise 'verwerfen' möchte. Eine einfache Lösung, wenn man nicht gleich den großen Hammer des Sektionskonzeptes (siehe Kapitel 3.8) schwingen möchte, sind temporäre Symbole, die solange ihre Gültigkeit behalten, bis ein neues, nicht-temporäres Symbol definiert wird. Andere Assembler bieten einen ähnlichen Mechanismus an, der dort unter dem Stichwort 'lokale Symbole' läuft, zur besseren Abgrenzung gegen das Sektionskonzept möchte ich aber beim Begriff 'temporäre Symbole' bleiben. AS kennt drei unterschiedliche Typen von temporären Symbolen, um jedem 'Umsteiger' ein Konzept anzubieten, das den Umstieg so einfach wie möglich macht. Leider kocht quasi jeder Assembler bei diesem Thema sein eigenes Süppchen, so daß es nur in Ausnahmefällen eine 1:1-Lösung für existierenden Code geben wird:
Ein Symbol, dessen Name mit zwei Dollarzeichen beginnt (dies ist weder für normale Symbole noch Konstanten zulässig), ist ein temporäres Symbol mit Namen. AS führt intern einen Zähler mit, der zu Beginn der Assemblierung auf Null gesetzt wird und bei jeder Definition eines nicht-temporären Symbols inkrementiert wird. Wird ein temporäres Symbol definiert oder referenziert, so werden die beiden führenden Dollarzeichen gestrichen und der momentane Stand des Zählers wird angehängt. Auf diese Weise erhält man mit jedem nicht-temporären Symbol sozusagen die Symbolnamen zurück - man kommt an die Symbole vor dieser Definition aber auch nicht mehr heran! Temporäre Symbole bieten sich daher besonders für den Einsatz in kleinen Anweisungsblöcken an, typischerweise etwa ein Dutzend Befehle, auf keinen Fall mehr als eine Bildschirmseite, sonst kommt man leicht durcheinander...
Hier ein kleines Beispiel:
$$loop: nop dbra d0,$$loop split: $$loop: nop dbra d0,$$loopWäre das zwischen den Schleifen liegende nicht-temporäre Label nicht vorhanden, gäbe es natürlich eine Fehlermeldung wegen eines doppelt definierten Symbols.
Namenlose Temporäre Symbole
Für all jene, denen temporäre Symbole mit Namen noch immer zu kompliziert sind, gibt es eine noch einfachere Variante: Setzt man als Label ein einfaches Plus- oder Minuszeichen, so werden diese in die Namen __forwnn bzw. __backmm umgesetzt, wobei nn bzw. mm von Null an laufende Zähler sind. Referenziert werden diese Symbole über die Sonderwerte - -- --- bzw. + ++ +++, womit sich die drei letzten 'Minussymbole' bzw die drei nächsten 'Plussymbole' referenzieren lassen. Welche Variante man benutzt, hängt also davon ab, ob man ein Symbol vorwärts oder rückwärts referenzieren will.
Bei der Definition namenloser temporärer Symbole gibt es neben dem Plus- und Minuszeichen noch eine dritte Variante, nämlich einen Schrägstrich (/). Ein so definiertes temporäres Symbol kann gleichermaßen vorwärts wie rückwärts referenziert werden; d. h. je nach Referenzierung wird es wie ein Minus oder Plus behandelt.
Namenlose temporäre Symbole finden ihre Anwendung üblicherweise in Konstruktionen, die auf eine Bildschirmseite passen, wie das bedingte Überspringen von ein paar Maschinenbefehlen oder kleinen Schleifen - ansonsten würde die Sache zu unübersichtlich werden (das ist aber nur ein gut gemeinter Rat...). Ein Beispiel dafür ist das folgende Stück Code, zur Abwechslung mal als 65xx-Code:
cpu 6502 - ldx #00 - dex bne - ; springe zu 'dex' lda RealSymbol beq + ; springe zu 'bne --' jsr SomeRtn iny + bne -- ; springe zu 'ldx #00' SomeRtn: rts RealSymbol: dfs 1 inc ptr bne + ; springe zu 'tax' inc ptr+1 + tax bpl ++ ; springe zu 'dex' beq + ; springe vorwaerts zu 'rts' lda #0 / rts ; Schraegstrich = Wildcard + dex beq - ; springe rueckwaerts zu 'rts' ptr: dfs 2
Dies ist vielleicht der Typ von temporären Symbolen, der dem Konzept von lokalen Symbolen und Sektionen am nächsten kommt. Wann immer der Name eines Symboles mit einem Punkt (.) anfängt, wird das Symbol nicht mit diesem Namen in der Symboltabelle abgelegt. Stattdessen wird der Name des zuletzt definierten Symbols ohne vorangestellten Punkt davorgehängt. Auf diese Weise nehmen Sybole, deren Name nicht mit einem Punkt anfängt, quasi die Rolle von 'Bereichsgrenzen' ein und Symbole, deren Name mit einem Punkt anfängt, können in jedem Bereich neu verwendet werden. Sehen wir uns das folgende kurze Beispiel an:
proc1: ; nicht-tempor"ares Symbol 'proc1' .loop moveq #20,d0 ; definiert in Wirklichkeit 'proc1.loop' dbra d0,.loop rts proc2: ; nicht-tempor"ares Symbol 'proc2' .loop moveq #10,d1 ; definiert in Wirklichkeit 'proc2.loop' jsr proc1 dbra d1,.loop rtsMan beachte, daß es weiterhin möglich ist, auf alle temporären Symbole zuzugreifen, auch wenn man sich nicht im gleichen 'Bereich' befindet, indem man einfach den zusammengesetzten Namen benutzt (wie z.B. 'proc2.loop' im voranstehenden Beispiel).
Zusammengesetzte Symbole lassen sich prinzipiell mit Sektionen kombinieren und können so auch zu lokalen Symbolen werden. Man beachte allerdings, daß das zuletzt definierte, nicht temporäre Symbol nicht pro Sektion gespeichert wird, sondern lediglich global. Das kann sich aber auch irgendwann einmal ändern, man sollte sich also nicht auf das augenblickliche Verhalten verlassen.
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.10).
Intel-Modus (Intel, Zilog, Thomson, Texas, Toshiba, NEC, Siemens, Philips, Fujitsu, Fairchild, Intersil) |
Motorola-Modus (Rockwell, Motorola, Microchip, Thomson, Hitachi) |
C-Modus (PowerPC, AMD29K, National, Symbios, Atmel) |
|
---|---|---|---|
dezimal hexadezimal binär oktal |
direkt nachgestelltes H nachgestelltes B nachgestelltes O nachgestelltes Q |
direkt vorangestelltes $ vorangestelltes % vorangestelltes @ |
direkt vorangestelltes 0x vorangestelltes 0b vorangestellte 0 |
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 Schreiben 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.9.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. Ebenfalls mit dieser Option eröffnet sich eine weitere, vierte Schreibweise für Integerkonstanten, wie man sie bei manchen Fremdassemblern antrifft: Bei dieser Schreibweise wird der eigentliche Wert in Hochkommas geschrieben und das Zahlensystem ('x' oder 'h' für hexadezimal, 'o' für oktal und 'b' für binär) direkt davor. Die Integer-Konstante 305419896 kann damit also folgendermaßen geschrieben werden:
x'12345678' h'12345678' o'2215053170' b'00010010001101000101011001111000'Diese Schreibweise ist für keine Prozessorarchitektur der Default und nur im RELAXED-Modus erreichbar. Sie dient in erster Linie der einfacheren Portierung von Fremdquellen und wird nicht für neu erstellte Programme empfohlen.
Wie bereits angesprochen, können Integer-Konstanten auch als ASCII-Werte geschrieben werden, so entsprechen
'A' == $41 'AB' == $4142 'ABCD' == $41424344Wichtig ist, daß hier die Zeichen in einfachen Hochkommas geschrieben werden, um sie von den weiter unten beschriebenen Stringkonstanten zu unterscheiden.
Gleitkommazahlen werden in der üblichen halblogarithmischen Schreibweise geschrieben, die in der allgemeinsten Form
[-]<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.
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:
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
Ü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
message "Wurzel aus 81 : \{sqrt(81)}"die Ausgabe
Wurzel aus 81 : 9Der 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. Integer-Ausdrücke werden defaultmäßig hexadezimal ausgegeben, dies läßt sich jedoch mit dem OUTRADIX-Befehl ändern.
Bis auf den Einbau von Formelausdrücken ist dieser Escape-Mechanismus auch in als ASCII definierten Integerkonstanten zulässig, z.B. so:
move.b #'\n',d0Jedoch 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',d0Nach 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.
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.11 genannten Operanden zur Verfügung.
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 NOT |
2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 1 |
ja ja ja ja ja ja ja ja ja ja ja ja ja ja*) ja ja ja ja ja ja ja ja ja |
ja ja ja ja ja ja nein nein nein nein ja ja nein ja ja ja nein nein nein nein nein nein nein |
ja ja ja ja ja ja nein nein nein nein nein ja nein nein nein nein nein nein nein nein nein nein nein |
14 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 |
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.12 und 2.13 auflisten.
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 |
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 |
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 |
Gleitkomma 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 |
Name | Funktion | Argument | Ergebnis |
---|---|---|---|
SGN ABS TOUPPER TOLOWER UPSTRING LOWSTRING STRLEN SUBSTR CHARFROMSTR STRSTR VAL EXPRTYPE |
Vorzeichen (0/1/-1) 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 extrahiert ein Zeichen aus einem String sucht Teilstring in einem String evaluiert Stringin- halt als Ausdruck liefert Typ des Arguments |
Integer oder Gleitkomma Integer oder Gleitkomma Integer Integer String String String String, Integer, Integer String, Integer String, String String Integer, Gleitkomma, String |
Integer Integer oder Gleitkomma Integer Integer String String Integer String Integer Integer abh. von Argument 0 1 2 |
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). Analog erwartet CHARFROMSTR den Quellstring als erstes Argument und die Zeichenposition als zweites Argument. Falls die angegebene Position größer oder gleich der Länge des Quellstrings ist, liefert SUBSTR einen Leerstring, während CHARFROMSTR eine -1 ergibt. Eine Position kleiner Null wird von SUBSTR als Null behandlet, während CHARFROMSTR in diesem Fall ebenfalls eine -1 liefert.
Hier ein Beispiel, wie man die beiden Funktionen einsetzt, um einen String im Speicher abzulegen, wobei das String-Ende durch ein gesetztes MSB gekennzeichnet ist:
dbstr macro arg if strlen(arg) > 1 db substr(arg, 0, strlen(arg) - 1) endif if strlen(arg) > 0 db charfromstr(arg, strlen(arg) - 1) | 80h endif endm
STRSTR liefert das erste Auftreten des zweiten Strings im ersten bzw. -1, falls das Suchmuster nicht gefunden wurde. Analog zu SUBSTR und CHARFROMSTR hat das erste Zeichen den Positionswert 0.
Wenn eine Funktion auch Gleitkommaargumente erwartet, so soll dies nicht bedeuten, daß man nicht z.B.
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.
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.9 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):
move.l #10,d0 loop: move.l (a1),d1 beq skip neg.l d1 skip: move.l d1,(a1+) dbra d0,loopDenkt 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.
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...):
cpu 6811 org $8000 beq skip rept 60 ldd Var endm skip: nop Var equ $10Aufgrund 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:
move.l #sym2,d0 sym2 equ sym1+5 sym1 equ 0so handelt man sich im zweiten Pass eine Fehlermeldung wegen eines undefinerten Symbols ein...aber warum machen Leute eigentlich solche Dinge ???
Zugegeben, das war ein ziemlich länglicher Ausflug, aber es mußte einfach einmal sein. Was sollte man als Erkenntnis aus diesem Abschnitt mitnehmen?
Gültigkeit: PowerPC, M-Core, 4004/4040, MCS-48/(2)51, 80C16x, AVR, XS1, Z8
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:
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...
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, CONSTANT nur KCPSM(3)
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:
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=4Intern werden Konstanten und Variablen identisch gespeichert, der einzige Unterschied ist, daß sie als unveränderbar markiert werden, wenn sie mit EQU definiert werden. Der Versuch, eine Konstante mit SET zu verändern, gibt eine Fehlermeldung.
Mit EQU/SET lassen sich Konstanten aller Typen definieren, z.B.
IntZwei EQU 2 FloatZwei EQU 2.0Einige Prozessoren besitzen leider bereits selber einen SET-Befehl. Bei diesen muß EVAL anstelle von SET verwendet werden.
Anstelle von EQU darf auch einfach ein Gleichheitszeichen geschrieben werden, analog kann man anstelle von SET bzw. EVAL einfach := schreiben. Des weiteren existiert eine 'alternative' Syntax, bei der der Synbolname nicht aus dem Feld für das Label genommen wird, sondern das erste Argument ist. Alternativ darf man also auch schreiben:
EQU IntZwei,2 EQU FloatZwei,2.0Das Feld für das Label muß in diesem Fall leer bleiben.
Aus Kompatibilitätsgründen zum Originalassembler gibt es für das KCPSM-Target auch den CONSTANT-Befehl, der im Gegensatz zu EQU Namen und Wert als Argument erwartet, also z.B. so:
CONSTANT const1, 2CONSTANT ist allerdings auf Integer-Konstanten beschränkt.
Defaultmäßig sind mit SET oder EQU definierte Symbole typenlos, optional kann jedoch als zweites obzw. drittes 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.
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.
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
<Name> label $erzeugt aber ein Symbol mit korrekten Attributen.
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
Mein_Carry bit PSW.7auf 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.15Beim 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.
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
bit1 BIT @h+5.2erlaubt.
Beim ST9 ist es hingegen möglich, Bits auch invertiert anzusprechen, was beim BIT-Befehl auch berücksichtigt wird:
invbit BIT r6.!3Näheres zum BIT-Befehl beim ST9 findet sich bei den prozessorspezifischen Hinweisen.
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
INT3 EQU P019 INT3_ENABLE DBIT 0,INT3das Bit, welches Interrupts von Anschluß INT3 freigibt. So definierte Bits können dann von den Befehlen SBIT0, SBIT1, CMPBIT, JBIT0 und JBIT genutzt werden.
Gültigkeit: 8080/8085/8086, XA, Z80, 320C2x/5x, 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 und 320C5x, 0..63 beim AVR und 0..255 beim Rest.
Beispiel: eine PIO 8255 liege auf Adresse 20H:
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
Gültigkeit: AVR, M*Core, ST9, 80C16x, KCPSM ( NAMEREG nur für KCPSM(3)), LatticeMico8, MSP430(X)
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.11 beschrieben sind.
NAMEREG existiert aus Kompatibilitätsgründen zum Originalassembler für den KCPSM. Es hat die gleiche Funktion, lediglich werden sowohl Register- als auch symbolischer Name als Argumente angegeben, z.B. so:
NAMEREG s08, treg
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.21.
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.
CHARSET 'ä',128daß 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.
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 'a',"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
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 SymA,SymB,SymCDieser Befehl weist den Symbolen SymA, SymB und SymC die Werte 0, 1 und 2 zu.
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:
ENUM Januar=1,Februar,März,April,Mai,JuniHier 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,OktoberDie 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
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:
pushv ,var1,var2,var3 . . popv ,var3,var2,var1AS verwendet dann einen internen, vordefinierten Default-Stack.
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.5). 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.
WICHTIG: Falls auch mit dem PHASE-Befehl gearbeitet wird, muß berücksichtigt werden, daß das Argument von ORG immer die Ladeadresse des Codes ist, nicht die Ausführungsadresse. Ausdrücke, die sich mit dem $- oder *-Symbol auf den aktuellen Programmzähler beziehen, liefern aber die Ausführungsadresse des Codes und führen als Argument von ORG nicht zum gewünschten Ergebnis. In solchen Fällen ist die RORG-Anweisung (3.2.2) das Mittel der Wahl.
Ziel Ziel |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO IO |
REG REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
68xxx | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
DSP56000/ DSP56300 |
64K/ 16M |
--- |
--- |
64K/ 16M |
64K/ 16M |
--- |
--- |
--- |
--- |
PowerPC | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
M*Core | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
6800,6301, 6811 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
6805/ HC08 |
8K/ 64K |
--- --- |
--- --- |
--- --- |
--- --- |
--- --- |
--- --- |
--- --- |
--- --- |
6809, 6309 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
68HC12(X), XGATE |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
68HC16 | 1M | --- | --- | --- | --- | --- | --- | --- | --- |
68RS08 | 16K | --- | --- | --- | --- | --- | --- | --- | --- |
H8/300 H8/300H |
64K 16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
H8/500 (Min) H8/500 (Max) |
64K 16M |
--- --- |
--- --- |
--- --- |
--- --- |
--- --- |
--- --- |
--- --- |
--- --- |
SH7000/ 7600/7700 |
4G |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
HD614023 HD614043 HD614081 |
2K 4K 8K |
160 256 512 |
--- --- --- |
--- --- --- |
--- --- --- |
--- --- --- |
16 16 16 |
--- --- --- |
--- --- --- |
6502, MELPS740 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
HUC6280 | 2M | --- | --- | --- | --- | --- | --- | --- | --- |
65816, MELPS- 7700 |
16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
Ziel Ziel |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO IO |
REG REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
MELPS- 4500 |
8K |
416 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
M16 | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
M16C | 1M | --- | --- | --- | --- | --- | --- | --- | --- |
4004 | 4K | 256 | --- | --- | --- | --- | --- | --- | --- |
8008 | 16K | 8 | --- | --- | --- | --- | --- | --- | --- |
MCS-48, MCS-41 |
4K |
--- |
256 |
256 |
--- |
--- |
--- |
--- |
--- |
MCS-51 | 64K | 256 | 256* | 64K | --- | 256 | --- | --- | --- |
80C390 | 16M | 256 | 256* | 16M | --- | 256 | --- | --- | --- |
MCS-251 | 16M | --- | --- | --- | --- | --- | 512 | --- | --- |
MCS-96 196(N)/ 296 |
64K 16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
8080, 8085 |
64K |
--- |
--- |
--- |
--- |
--- |
256 |
--- |
--- |
80x86, | 64K | 64K | --- | 64K | --- | --- | 64K | --- | --- |
68xx0 | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
8X30x | 8K | --- | --- | --- | --- | --- | --- | --- | --- |
2650 | 8K | --- | --- | --- | --- | --- | --- | --- | --- |
XA | 16M | 16M | --- | --- | --- | --- | 2K- | --- | --- |
AVR | 8K | 64K | --- | --- | --- | --- | 64 | --- | --- |
29XXX | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
80C166, 80C167 |
256K 16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
* Initialwert 80h. 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! |
|||||||||
- Initialwert 400h. |
Ziel Ziel |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO IO |
REG REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
Z80, Z180, Z380 |
64K 512K+ 4G |
--- |
--- |
--- |
--- |
--- |
256 256 4G |
--- |
--- |
Z8 | 64K | 256 | --- | --- | --- | --- | --- | --- | --- |
eZ8 | 64K | 256 | --- | 64K | --- | --- | --- | --- | --- |
KCPSM | 256 | 256 | --- | --- | --- | --- | --- | --- | --- |
KCPSM3 | 256 | 64 | --- | --- | --- | --- | 256 | --- | --- |
Mico8 | 4096 | 256 | --- | --- | --- | --- | 256 | --- | --- |
TLCS- 900(L) |
16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TLCS-90 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
TLCS- 870 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TLCS-47 | 64K | 1K | --- | --- | --- | --- | 16 | --- | --- |
TLCS- 9000 |
16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TC9331 | 320 | --- | --- | --- | --- | --- | --- | --- | --- |
PIC 16C5x |
2K |
32 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
PIC 16C5x |
2K |
32 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
PIC 16C64, 16C86 |
8K |
512 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
PIC 17C42 |
64K |
256 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
ST6 | 4K | 256 | --- | --- | --- | --- | --- | --- | --- |
ST7 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
ST9 | 64K | 64K | --- | --- | --- | --- | --- | 256 | --- |
6804 | 4K | 256 | --- | --- | --- | --- | --- | --- | --- |
32010 32015 |
4K 4K |
144 256 |
--- |
--- |
--- |
--- |
8 8 |
--- |
--- |
320C2x | 64K | 64K | --- | --- | --- | --- | 16 | --- | --- |
Ziel Ziel |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO IO |
REG REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
320C3x | 16M | --- | --- | --- | --- | --- | --- | --- | --- |
320C40 | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
320C44 | 32M | --- | --- | --- | --- | --- | --- | --- | --- |
320C5x/ 320C20x/ 320C54x |
64K |
64K |
--- |
--- |
--- |
--- |
64K |
--- |
--- |
TMS 9900 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TMS 70Cxx |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
370xxx |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
MSP430 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TMS1000 TMS1200 |
1K |
64 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TMS1100 TMS1300 |
2K |
128 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
SC/MP | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
807x | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
COP4 | 512 | --- | --- | --- | --- | --- | --- | --- | --- |
COP8 | 8K | 256 | --- | --- | --- | --- | --- | --- | --- |
ACE | 4K* | --- | --- | --- | --- | --- | --- | --- | --- |
µPD 78(C)10 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
7566 | 1K | 64 | --- | --- | --- | --- | --- | --- | --- |
7508 | 4K | 256 | --- | --- | --- | --- | 16 | --- | --- |
75K0 | 16K | 4K | --- | --- | --- | --- | --- | --- | --- |
78K0 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
78K0 | 1M | --- | --- | --- | --- | --- | --- | --- | --- |
* Initialwert 800h bzw. 0C00h |
Ziel Ziel |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO IO |
REG REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
7720 |
512 |
128 |
--- |
--- |
--- |
--- |
--- |
--- |
512 |
7725 |
2K |
256 |
--- |
--- |
--- |
--- |
--- |
--- |
1024 |
77230 | 8K | --- | --- | 512 | 512 | --- | --- | --- | 1K |
53C8XX | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
F2MC8L | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
F2MC16L | 16M | --- | --- | --- | --- | --- | --- | --- | --- |
MSM5840 | 2K | 128 | --- | --- | --- | --- | --- | --- | --- |
MSM5842 | 768 | 32 | --- | --- | --- | --- | --- | --- | --- |
MSM58421 MSM58422 |
1.5K |
40 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
MSM5847 | 1.5K | 96 | --- | --- | --- | --- | --- | --- | --- |
MSM5054 | 1K | 62 | --- | --- | --- | --- | --- | --- | --- |
MSM5055 | 1.75K | 96 | --- | --- | --- | --- | --- | --- | --- |
MSM5056 | 1.75K | 90 | --- | --- | --- | --- | --- | --- | --- |
MSM6051 | 2.5K | 119 | --- | --- | --- | --- | --- | --- | --- |
180x | 64K | --- | --- | --- | --- | --- | 8 | --- | --- |
XS1 | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
1750 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
Gültigkeit: alle Prozessoren
RORG setzt wie ORG den Programmzähler neu, erwartet als Argument allerdings keine absolute Adresse, sondern einen relativen Wert (positiv oder negativ), der zum Programmzähler addiert wird. Eine Anwendungsmöglichkeit ist das Freilassen einer bestimmten Menge von Adreßraum, oder die Anwendung in Code-Teilen, die an mehreren Stellen (z.B. via Makros oder Includes) eingebunden werden und lageunabhängig arbeiten sollen. Eine weitere Anwendungsmöglichkeit ergibt sich in Code, der eine Ausführungsadresse unterschiedlich zur Ladeadresse hat (d.h. es wird mit der PHASE-Anweisung gearbeitet). Es gibt kein Symbol, über das man in so einer Situation auf die aktuelle Ladeadresse zugreifen kann, aber mittels RORG kann man sich indirekt darauf beziehen.
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:
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 --> 56300Wä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 -> PPC403GC -> MPC505 -> MPC601 -> MPC821 -> RS6000Der 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. Die GC-Variante des PPC403 hat zusätzlich eine MMU und deshalb einige Befehle zu deren Steuerung mehr. Der MPC505 (eine Mikrokontroller-Variante mit FPU) unterscheidet sich solange vom 601er nur in den Peripherieregistern, wie ich es nicht besser weiß - [63] 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) XGATE
f) 6800 -> 6301 -> 6811Während der 6301 nur neue Befehle definiert, liefert der 6811 neben weiteren Befehlen ein zweites Indexregister Y zur Adressierung.
g) 6809/6309 und 6805/68HC08/68HCS08Diese 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 [41]).
h) 68HC12 --> 68HC12XDer 12X-Kern bietet eine Reihe neuer Befehle, bzw. bestehende Befehle wurden um neue Adressierungsarten ergänzt.
i) 68HC16
j) HD6413308 --> HD6413309Diese 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.
k) HD6475328 --> HD6475348 --> HD6475368 --> HD6475388Diese Prozessoren besitzen alle den gleichen CPU-Kern; Die unter- schiedlichen Typen dienen lediglich der Einbindung des korrekten Registersatzes in der Datei REG53X.INC.
l) SH7000 --> SH7600 --> SH7700Der 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.
m)HD614023 --> HD614043 --> HD614081Diese drei Varianten der HMCS400-Serie unterscheiden sich in der Größe des internen ROM- und RAM-Speichers.
n) 6502 -> 65(S)C02 -> W65C02S / 65C19 / MELPS740 / HUC6280 / 6502UNDOCDie CMOS-Version definiert einige zusätzliche Befehle, außerdem sind bei einigen Befehlen Adressierungsarten hinzugekommen, die beim 6502 nicht möglich waren. Der W65SC02 ergänzt den 65C02-Befehlssatz um zwei Befehle, mit denen die Low-Power-Modi der CPU feiner eingestellt werden können. Dem 65SC02 fehlen die Bitmanipulationsbefehle des 65C02.
Der 65C19 ist nicht] binär aufwärtskompatibel zum
originalen 6502! Einige Adressierungsarten wurden durch andere
ersetzt. Des weiteren enthält dieser Prozessor
Befehlssatzerweiterungen, die die Implementierung digitaler
Signalverarbeitung erleichtern.
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 Akkumulators sind die
Erweiterungen disjunkt.
Das herausstechendste Merkmal des HuC 6280 ist der größere
Adre"raum von 2 MByte anstelle 64 KByte, der durch eingebaute
Bankregister erreicht wird. Des weiteren existieren einige
Sonderbefehle zur Kommunikation mit dem Videoprozessor (dieser Chip
wurde in Videospielen eingesetzt) und zum Kopieren von
Speicherbereichen.
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.
Beim CPU-Befehl muß der Prozessortyp als einfaches Literal
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, sofern dieser
über die gleichnamige Kommandozeilenoption nicht verändert
wurde.
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, 68xx, M*Core, XA, H8, SH7000, TMS9900,
MSP430(X), 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: AVR
PACKING ist in gewisser Weise ähnlich zu
PADDING, es arbeitet nur gewissermaßen anders herum:
während PADDING die abgelegten Daten ergänzt, um
komplette Worte und damit ein Alignment zu erhalten, quetscht
PACKING mehrere Werte in ein einzelnes Wort. Dies macht im
Code-Segment des AVR Sinn, weil dort mit einem Spezialbefehl (
LPM) auf einzelne Bytes in den 16-Bit-Worten zugegriffen werden
kann. Ist diese Option eingeschaltet (Argument ON), so
werden immer zwei Byte-Werte bei DATA in ein Wort gepackt,
analog zu den einzelnen Zeichen von String-Argumenten. Der
Wertebereich der Integer-Argumente reduziert sich dann natürlich
auf -128...+255. Ist diese Option dagegen ausgeschaltet,
(Argument OFF), so bekommt jedes Integer-Argument sein
eigenes Wort und darf auch Werte von -32768...+65535 annehmen.
Diese Unterscheidung betrifft nur Integer-Argumente von
DATA, Strings werden immer gepackt. Zu beachten ist weiterhin,
daß dieses Packen nur innerhalb der Argumente eines
DATA-Befehls funktionieren kann, wer also mehrere
DATA-Befehle hinterienander hat, fängt sich bei ungeraden
Argumentzahlen trotzdem halbvolle Wörter ein!
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: Atmel AVR
Ist dieser Schalter auf ON gesetzt, so veranlaßt man
AS dazu, anzunehmen, der Programmzähler des Prozessors habe
nicht die volle, durch die Architektur gegebene Länge von 16
Bits, sondern nur eine Länge, die es gerade eben erlaubt, das
interne ROM zu adressieren. Im Falle des AT90S8515 sind dies z.B. 12
Bit, entsprechend 4 KWorten oder 8 KBytes. Damit werden relative
Sprünge vom Anfang des ROMs zum Ende und umgekehrt möglich,
die bei strenger Arithmetik einen out-of-branch ergeben würden,
hier jedoch funktionieren, weil die Übertragsbits bei der
Zieladressenberechnung 'unter den Tisch' fallen. Vergewissern Sie
sich genau, ob die von Ihnen eingesetzte Prozessorvariante so
arbeitet, bevor Sie diese Option einschalten! Im Falle des oben
erwähnten AT90S8515 ist diese Option sogar zwingend nötig,
um überhaupt quer durch den ganzen Adreßraum springen zu
können...
Defaultmäßig steht dieser Schalter auf OFF, der
momentane Stand läßt sich aus einem gleichnamigen Symbol
auslesen.
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:
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:
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:
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:
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!
Ein mit ASSUME definierter Wert läßt sich mit der
eingebauten Funktion ASSUMEDVAL wieder abfragen oder in
Ausdrücke einbauen. Dies gilt für alle im folgenden
gelisteten Architekturen mit Ausnahme des 8086.
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...
Auch beim HC11 konnten die Entwickler letzten Endes nicht dem
'Sündenfall' widerstehen und haben in den K4 ein Banking-Schema
eingebaut, um mit 16 Adreßleitungen mehr als 64 Kbyte
anzusprechen. Die Register MMSIZ, MMWBR,
MM1CR und MM2CR legen fest, ob wie die beiden
zusätzlichen 512K-Bereiche in den physikalischen Adreßraum
eingeblendet werden sollen. Initial nimmt AS den Reset-Zustand dieser
Register an, d.h. alle mit $00 belegt und das Windowing ist
abgeschaltet.
Wie die Variante ohne anhängendes 'X' kennt auch der HC12X eine
kurze direkte Adressierungsart, die hier jedoch auch andere
Adreßbereiche als die ersten 256 Byte erreichen kann. Über
das DIRECT-Register kann die 256-Byte-Seite vorgegeben
werden, die mit dieser kurzen Adressierungsart angesprochen wird.
Mittels ASSUME wird AS der momentane Stand dieses Registers
mitgeteilt, so daß bei absoluten Adressen automatisch die
effizienteste Adressierungsart gewählt werden kann. Default ist
0, was auch dem Reset-Zustand entspricht.
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.
Diese Prozessoren beinhalten eine Reihe von Registern, deren Inhalt
AS kennen muß, um den korrekten Code zu erzeugen. Es handelt
sich um folgende Register:
Um mich nicht in endlose Wiederholungen zu ergehen, verweise ich
für die Benutzung dieser Werte auf Kapitel 4.12. 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.
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 :
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:
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
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
Architektur und Befehlssatz dieser Prozessoren sind grob an den Intel
8080/8085 angelehnt - das gilt auch für die Mnemonics. Die
Adressierungsart (direkt, indirekt, immediate) ist mit in das
Mnemonic verpackt, und 16-Bit-Register (BC, DE, HL) werden wie beim
8080 mit einem Buchstaben abgekürzt. Da NEC in der
Erklärung der einzelnen Adressierungsarten aber auch immer
wieder die ausgeschriebenen Registernamen benutzt und auch mal
Klammern benutzt, um indirekte Adressierung anzudeuten und mal nicht,
habe ich mich entschlossen, neben den 'offiziellen' Notationen aus
dem NEC-Manual auch einige alternative Notationen zuzulassen. Einige
nicht-NEC-Tools wie z.B. Disassembler scheinen auch solche Notationen
zu benutzen:
78K2 ist eine 8/16-Bit-Architektur, die nachträglich durch
Banking auf einen (Daten-)Adreßraum von einem MByte erweitert
wurde. Das Banking wird mit den Registern PM6 (Normalfall) bzw. P6
(alternativer Fall mit vorangestelltem &) realisiert, die die
fehlenden oberen vier Bits nachliefern. Zumindest bei absoluten
Adressen kann AS überprüfen, ob die gerade angesprochene,
lineare 20-bittige Adresse innerhalb des gegebenen 64K-Fensters
liegt.
Prozessoren mit 78K3-Kern besitzen Registerbänke mit insgesamt
16 Registern, die man über ihre Nummern ansprechen kann
(R0 bis R15) oder ihre symbolischen Namen
(X=R0, A=R1, C=R2, B=R3, VPL=R8, VPH=R9, UPL=R10, UPH=R11, E=R12,
D=R13, L=R14, H=R15). Der Prozessorkern besitzt ein
Register-Auswahlbit (RSS), mit dem man das Mapping von A/X
und B/C von R0..R3 auf R4..R7 umschaltet. Dies ist in erste Linie
für Befehle wichtig, die implizit eines dieser Register benutzen
(d.h. bei denen die Registernummer nicht im Maschinenbefehl kodiert
ist). Man kann dem Assembler aber auch über ein
mitteilen, daß die folgenden Befehle mit diesem geänderten
Mapping arbeiten. Der Assembler wird für Befehle, in denen die
Registernummer explizit kodiert ist, dann auch die alternativen
Registernummern einsetzen. Umgekehrt wird dann z.B. auch R5
statt R1 im Quellcode wie A behandelt.
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/16 Bit aus dem
DP-Register ergänzt werden. Bei Adressierungen kann man aber
trotzdem die volle 24/32-Bit-Adresse angeben, AS prüft dann, ob
die oberen 8/16 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.
Wie viele andere Mikrokontroller auch, leidet diese Familie etwas
unter der Knauserei seiner Entwickler: einem 24 Bit breiten
Adreßraum stehen 16 Bit breite Adreßregister etwas
unterbemittelt gegenüber. Also mußten wieder mal
Bank-Register her. Im einzelnen sind dies PCB für den
Programmcode, DTB für alle Datenzugriffe, ADB für indirekte
Zugriffe über RW2/RW6 und SSB/USB für die Stacks. Sie
können alle Werte zwischen 0 und 255 annehmen.
Defaultmäßig stehen alle Annahmen von AS auf 0, mit
Ausnahme von 0ffh für PCB.
Des weiteren existiert das DPR-Register, das angibt, welche Seite
innerhalb der durch DTB gegebenen 64K-Bank mit 8-Bit-Adressen
erreicht werden kann. Der Default für DPR ist 1, zusammen mit
dem Default für DTB ergibt dies also eine Default-Seite bei
0001xxh.
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
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
Gültigkeit: 8080/8085
Mit ON als Argument kann man (fast) alle 8080-Befehle
wahlweise auch in der Form schreiben, wie sie Zilog für den Z80
definiert hat. Zum Beispiel benutzt man einfach nur noch LD
mit sich selbst erklärenden Operanden, wo man in der originalen
8080-Syntax je nach Operanden MVI, LXI, MOV, STA, LDA, SHLD,
LHLD, LDAX, STAX oder SPHL schreiben muß.
Weil einige Mnemonics in der 8080- und Z80-Syntax unterschiedliche
Bedeutung haben, kann man nicht zu 100% im 'Z80-Stil' programmieren.
Wo die Grenzen dieser Betriebsart liegen, kann man im Detail im
Abschnitt 4.18 nachlesen.
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.
Es ist im Gegensatz zum Original Motorola-Assembler auch erlaubt, mit
diesem Kommando Speicher zu reservieren, indem man als Argument ein
Fragezeichen angibt. Diese Erweiterung haben wohl einige
Drittanbieter von 68K-Assemblern eingebaut, in Anlehnung an das, was
Intel-Assembler machen. Wer dies benutzt, sollte sich aber im klaren
sein, daß dies zu Problemen beim Portieren von Code auf andere
Assembler führen kann. Des weiteren dürfen Fragenzeichen
als Operanden nicht mit 'normalen' Konstanten in einer Anweisung
gemischt 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:
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 COP4/8 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!
Dieser Befehl stellt eine Kurzschreibweise dar, um Speicherbereiche
zu reservieren:
DS8 ist beim National SC14xxx als Alias für
DS definiert. Achten Sie aber darauf, daß der Speicher
dieser Prozessoren in Worten zu 16 Bit organisiert ist, d.h. es ist
unmöglich, einzelne Bytes zu reservieren. Falls das Argument
von DS ungerade ist, wird es auf die
nächstgrößere gerade Zahl aufgerundet.
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, 320C2(0)x, 320C5x, MSP, TMS9900
Dito. Ein im 320C2(0)x/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: SC144xx
Dieser Befehl ist ein Alias für DB, d.h. mit ihm
können Byte-Konstanten oder Strings im Speicher abgelegt 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, 320C2(0)x, 320C3x/C4x/C5x, MSP
Für den 320C3x/C4x und i960 werden hiermit 32-Bit-Worte
abgelegt, für die alle anderen Familien 16-Bit-Worte. Ein im
320C2(0)x/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: SC144xx
Diser Befehl ist beim SC144xx der Weg, Konstanten mit Wortlänge
(16 Bit) im Speicher abzulegen und damit ein ALIAS für DW.
Gültigkeit: 320C2(0)x, 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/C4x (nicht DOUBLE), 320C6x
(nicht EXTENDED)
Mit diesen Befehlen werden Gleitkomma-Konstanten im Speicher
abgelegt, jedoch beim 320C3x/C4x 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: 320C2(0)x, 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).
Gültigkeit: 320C2(0)x, 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: 320C2(0)x, 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:
Gültigkeit: PIC, 320xx, AVR, MELPS-4500, HMCS400, 4004/4040,
µPD772x, OLMS-40/50
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, beim 4500er, 4004 und
HMCS400 im Code-Segement 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/C4x sogar derer 4 (MSB zuerst). Im Gegensatz dazu
muß im Datensegment des 4500 bzw. ein Zeichen auf zwei
Speicherstellen verteilt werden, ebenso wie beim 4004 und HMCS400.
Der Wertebereich für Integers entspricht der Wortbreite des
jeweiligen Prozessors im jeweiligen Segment. Das bedeutet,
daß DATA beim 320C3x/C4x 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: COP4/8
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: 320C2(0)x, 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.
Gültigkeit: PIC, MELPS-4500, HMCS400, 3201x, 320C2(0)x, 320C5x,
AVR, µPD772x, OLMS-40/50
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 und OLMS-40/50 Nibbles sowie bei Texas
ebenfalls Wörter.
Gültigkeit: 320C2(0)x, 320C3x/C4x/C5x/C6x, MSP
BSS arbeitet analog zu RES, lediglich ein
eventuell vor dem Befehl stehendes Symbol wird beim 320C2(0)x/5x als
untypisiert gespeichert. Der Sinn dieser Maßnahme kann im
Kapitel mit den prozessorspezifischen Hinweisen nachgelesen werden.
Gültigkeit: COP4/8
Diese beiden Befehle stellen im COP4/8-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: SC144xx
Dieser Befehl reserviert Speicher in Schritten von vollständigen
Worten, d.h. 16 Bit. Er stellt einen Alias zu DW dar.
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:
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
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.
Für jeden Makroparameter kann ein Defaultwert mit
angehängtem Gleichheitszeichen angegeben werden. Dieser Wert
wird für den Parameter eingesetzt, wenn beim Makroaufruf kein
Argument für diesen Parameter angegeben wird, bzw. wenn ein
Positionsargument (s.u.) für diesen Parameter leer ist.
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:
Argumente an ein Makro können in zwei Formen angegeben werden:
als Positionsargumente oder als Schlüsselwortargumente.
Bei Positionsargumenten ergibt sich die Zuordnung von Argumenten zu
Makro-Parametern einfach durch ihre Position in der Aufrufliste, d.h.
das erste Argument wird dem ersten Parameter zugeordnet, das zweite
Argument dem zweiten Parameter usw.. Werden weniger Argumente
angegeben als das Makro Parameter hat, werden eventuell definierte
Defaultwerte oder ein Leerstring eingesetzt. Gleiches gilt auch
für leere Argumente.
Schlüsselwortargumente geben jedoch explizit an, für
welchen Makro-Parameter sie gelten, indem der Parametername dem Wert
vorangestellt wird, z.B. so:
Im Unterschied zu Positionsargumenten ist es mit
Schlüsselwortargumenten auch möglich, einem Parameter einen
Leerstring zuzuweisen, der einen nicht-leeren Default-Wert hat.
Positions- und Schlüsselwortargumente dürfen auch in einem
Aufruf gemischt werden, jedoch dürfen ab dem ersten
Schlüsselwortargument keine Positionsargumente mehr verwendet
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:
Neben den am Makro selber angegebenen Parametern existieren vier
weitere 'implizite' Parameter, die immer vorhanden sind und daher
nicht als eigene Makroparameter verwendet werden sollten:
Der Zweck, ein Label 'intern' im Makro verwenden zu können, ist
sicher nicht unmittelbar einleuchtend. Den einen oder anderen Fall
mag es ja geben, in dem es sinnvoll ist, den Einsprungpunkt in ein
Makro irgendwo in seinen Rumpf zu verschieben. Der wichtigste
Anwendungsfall sind aber TI-Signalprozessoren, die eine
Parallelisierung von Befehlen durch einen doppelten senkrechten
Strich in der Label-Spalte kennzeichnen, etwa so:
Rekursion von Makros, also das wiederholte Aufrufen eines Makros
innerhalb seines Rumpfes oder indirekt über andere von ihm
aufgerufene Makros ist vollkommen legal. Wie bei jeder Rekusion
muß man dabei natürlich sicherstellen, daß sie
irgendwann ein Ende findet. Für den Fall, daß man dies
vergessen hat, führt AS in jedem definierten Makro einen
Zähler mit, der bei Beginn einer Makroexpansion inkrementiert
und an deren Ende wieder dekrementiert wird. Bei rekursiven Aufrufen
eines Makros erreicht dieser Zähler also immer höhere
Werte, und bei einem per NESTMAX einstellbaren Wert bricht
AS ab. Vorsicht, wenn man diese Bremse abschaltet: der Speicherbedarf
auf dem Heap kann so beliebig steigen und selbst ein Unix-System in
die Knie zwingen...
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:
In Makrorümpfen definierte Labels werden immer als lokal
betrachtet, außer bei der Definition des Makros wurde die
GLOBALSYMBOLS-Option verwendet. Ist es aus irgendwelchen
Gründen erforderlich, ein einzelnes Label in einem Makro global
zu machen, das ansonsten lokale Labels benutzt, so kann man es
mit LABEL definieren, dessen Anwendung (wie bei
BIT,SFR...) immer globale Symbole ergibt :
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:
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
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:
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:
Ob Symbole für jede einzelne Repetition lokal sind oder nicht,
kann wiederum durch die Steuerparameter GLOBALSYMBOLS
bzw. NOGLOBALSYMBOLS (durch geschweifte Klammern als solche
gekennzeichnet) bestimmt werden.
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:
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!
SHIFT ist ein Mittel, um Makros mit variablen
Argumentlisten abzuarbeiten: Es verwirft den ersten Parameter, so
daß der zweite Parameter seinen Platz einnimmt usw. Auf diese
Weise könnte man sich durch eine variable Argumentliste
durcharbeiten...wenn man es richtig macht. Folgendes funktioniert zum
Beispiel nicht...
Mit MAXNEST kann man einstellen, wie oft ein Makro maximal
rekursiv aufgerufen werden kann, bevor AS mit einer Fehlermeldung
abbricht. Dies darf ein beliebiger ganzer, positiver Wert sein, wobei
der Sonderwert 0 diese Sicherheitsbremse komplett abschaltet
(vorsicht damit...). Der Vorgabewert für die maximale
Verschachtelungstiefe ist 256; die momentante Einstellung kann aus
ener gleichnamigen Variablen gelesen werden.
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
Auch in Assemblerprogrammen ergibt sich dann und wann die
Notwendigkeit, analog zu Hochsprachen zusammengesetzte
Datenstrukturen zu definieren. AS unterstützt sowohl die
Definition als auch die Nutzung von Strukturen mit einer Reihe von
Konstrukten und Anweisungen, die im folgenden erläutert werden
sollen:
Die Definition einer Struktur wird duch den Befehl STRUCT
eingeleitet und durch ENDSTRUCT abgeschlossen (schreibfaule
Zeitgenossen dürfen aber auch stattdessen STRUC
bzw. ENDSTRUC oder ENDS schreiben). Ein eventuell
diesen Befehlen voranstehendes Label wird als Name der zu
definierenden Struktur genommen; am Ende der Definition ist der Name
optional und kann von zur Festlegung des Längennamens (s.u.)
genutzt werden. Das restliche Verfahren ist simpel: Mit einem
STRUCT wird der momentane Programmzähler gesichert und auf
Null zurückgesetzt. Alle zwischen STRUCT und
ENDSTRUCT definierten 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. Es gelten hier
auch gleichfalls die Regeln für das Aufrunden von Längen,
um Alignments zu erhalten - wer also 'gepackte' Strukturen definieren
will, muß eventuell ein PADDING OFF voranstellen.
Umgekehrt lassen sich Ausrichtungen natürlich mit Befehlen
wie ALIGN erzwingen.
Da eine solche Definition nur eine Art 'Prototypen' darstellt,
können nur Befehle benutzt werden, die Speicherplatz
reservieren, aber keine solchen, die Konstanten im Speicher ablegen
oder Code erzeugen.
Innerhalb von Strukturen definierte Labels (also die Namen der
Elemente) werden nicht direkt abgespeichert, sondern es wird ihnen
der Name der Struktur vorangestellt, durch ein Trennzeichen
verbunden, bei dem es sich defaultmäßig um den Unterstrich
(_) handelt. Dieses Verhalten läßt sich aber durch dem
STRUCT-Befehl mitgegebene Argumente steuern:
Neben den Namen der Elemente definiert AS beim Abschluß der
Definition ein weiteres Symbol mit dem Namen LEN, das nach
dem gleichen Regeln um den Namen der Struktur erweitert wird - oder
um den Label-Namen, der optional bei ENDSTRUCT angegeben
werden kann.
Das ganze sieht dan in der Praxis z.B. so aus:
Ist eine Struktur einmal definiert, ist die Nutzung denkbar einfach
und ähnlich wie ein Makro: ein einfaches
ACHTUNG! Obwohl AS keine Argumente bei der Definition einer
Struktur erwartet, werden gegebene Argumente nicht als Fehler
gemeldet, sondern schlicht ignoriert. Dies ist vorgesehen, um in
Zukunft einer deklarierten Struktur direkt Werte zuweisen zu
können.
Es ist ohne weiteres erlaubt, eine bereits definierte Struktur in
einer anderen Struktur aufzurufen. Das dabei ablaufende Verfahren ist
eine Kombination aus den beiden vorigen Punkten: Elemente der
Substruktur werden definiert, mit dem Namen dieser Instanz
vorangestellt, und vor diese zusammengesetzten Namen wird wieder der
Name der Struktur bzw. später bei einer Benutzung gesetzt. Das
sieht dann z.B. so aus:
Ebenso ist es erlaubt, eine Struktur direkt in einer anderen Struktur
zu definieren:
Eine Union ist eine Sonderform einer Struktur, bei der die einzelnen
Elemente nicht hintereinander, sondern übereinander liegen, d.h.
alle Elemente liegen an Startadresse 0 innerhalb der Struktur und
belegen den gleichen Speicherplatz. Naturgemäß tut so eine
Definition nicht mehr, als einer Reihe von Symbolen den Wert Null
zuzuweisen, sie kann aber sinnvoll sein, um programmtechnisch die
Überlappung der Elemente zu verdeutlichen und den Code so etwas
'lesbarer' zu gestalten. Die Größe einer Struktur ist das
Maximum der Größen aller Elemente.
Der Name einer Struktur oder Union ist optional, allerdings nur, wenn
diese Teil einer anderen, nicht namenlosen Struktur ist. Elemente
dieser Struktur werden dann Teil der 'nächsthöheren'
benamten Struktur:
Des weiteren wird für namenlose Strukturen oder Unions kein
Symbol mit deren Länge angelegt.
Im Verlaufe der Definition oder der Nutzung von Strukturen definierte
Symbole werden genauso behandelt wie normale Symbole, d.h. bei der
Nutzung innerhalb einer Sektion werden diese Symbole als lokal zu
dieser Sektion definiert. Analoges gilt aber auch für die
Strukturen selber, d.h. eine innerhalb einer Sektion definierte
Struktur kann nicht auërhalb der Sektion benutzt werden.
Will man Strukturen über Makros instantiieren, so muß man
die GLOBALSYMBOLS-Option bei der Definition des Makros
benutzen, damit die darüber erzeugten Symbole auch
außerhalb des Makros verwendbar sind. Eine Reihe von Strukturen
kann man z.B. so anlegen:
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....
Zu jeder IF...-Anweisung gehört ein entsprechendes
ENDIF, 'offene' Konstrukte führen zu einer Fehlermeldung am
Ende des Assemblierungslaufes. Die Zuordnung, welches ENDIF
AS mit welchem IF... 'gepaart' hat, läßt sich im
Listing erkennen: dort wird die Zeilennummer des entsprechenden
IFs angezeigt.
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.
Ist SWITCH auf dem gewählten Target ein
Maschinenbefehl, so leitet man das Konstrukt stattdessen mit
SELECT ein.
Ähnlich wie bei IF...-Konstrukten, muß es
für jedes SWITCH genau ein ENDCASE geben.
Analog zu ENDIF wird bei ENDCASE im Listing die
Zeilennummer des korrespondierenden SWITCH angezeigt.
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.
Falls PAGE auf dem gewählten Target bereits ein
Maschinenbefehl ist, benutzt man stattdessen PAGESIZE.
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:
Ist ein Makro einmal ausgestestet und 'fertig', möchte man es
bei Benutzung vielleicht gar nicht mehr im Listing sehen. Mit diesem
Pseudobefehl kann man steuern, ob der aus einem Makro expandierte
Code ausgegeben wird, und wenn ja, zu welchen Teilen. Im einfachsten
Fall schaltet ein
MACEXP kennt neben diesem globalen Ein- und Ausschalter
noch Wege, nur Teile des expandierten Makros zu listen. Dazu werden
die Zeilen in einem Makrorumpf in drei Klassen eingeteilt:
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 eigene
Flags besitzen, die anzeigt, welche Teile bei einer 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
(NO)EXPAND/(NO)EXPMACRO/(NO)EXPIF ü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.9.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!
OUTRADIX is gewissermaßen das Gegenstück zu
RADIX: Mit ihm kann man festlegen, in welchem Zahlensystem
berechnete Integer-Ausdrücke in Strings eingesetzt werden
sollen, wenn man \{...}-Konstrukte in Stringkonstanten verwendet
(siehe Abschnitt 2.9.3). Als
Argument sind wieder Werte zwischen 2 und 36 erlaubt; der Default ist
16.
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...
Mit der K4-Version des HC11 hat Motorola ein Banking-Schema
eingeführt, mit dem man zwar einerseits eine zu klein gewordene
Architektur noch einmal aufbohren kann, den Software- und
Tool-Entwicklern aber nicht unbedingt das Leben einfacher macht...wie
stellt man so etwas vernünftig dar?
Die K4-Architektur erweitert den Adreßraum des HC11 um 2x512
Kbyte, so daß jetzt insgesamt 64+1024=1088 Kbyte zur
Verfügung stehen. AS tut so, als ob es sich dabei um einen
Adreßraum handeln würde, der folgendermaßen
organisiert ist:
Wer sich nicht ganz sicher ist, ob die momentane Einstellung korrekt
ist, kann den Pseudobefehl PRWINS benutzen, der dann z.B.
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 [62] 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 [57] 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:
Beim Befehlssatz dieser 4-Bit-Prozessoren fühlte ich mich
spontan an den 8080/8085 erinnert - sehr viele Menemonics, die
Adressierungsart (z.B. indirekt oder direkt) ist in den Befehl
einkodiert, die Befehle sind zum Teil nur schwer zu merken.
Natürlich unterstützt AS diese Syntax, wie Hitachi sie
seinerzeit definiert hat, ich habe aber zusätzlich für die
meisten Befehle eine - finde ich - schönere und besser lesbare
Variante implementiert, so wie Zilog es seinerzeit mit den Z80
gemacht hat. Zum Beispiel können alle Maschineninstruktionen,
die in irgendeiner Form Daten transferieren, egal ob die Operanden
Register, Konstanten oder Speicherstellen sind, über den
AS-spezifischen LD-Befehl angesprochen werden. Ähnliche
'Meta-Befehle' gibt es für arithmetische und logische Befehle.
Eine vollständige Liste aller Meta-Befehle und ihrer Operanden
findet sich in den Tabellen 4.6 und
4.6, ihre praktische Verwendung
kann man sich in der Datei t_hmcs4x.asm ansehen.
Ähnlich wie beim HMCS400 sind die Adressierungsarten zu einem
großen Teil in die Mnemonics hineinkodiert, und ich habe mich
auch hier dafür entschieden, für häufig genutzte
Befehle eine alternative, modernere und besser lesbare Notation
bereitzustellen. Eine vollständige Liste aller Meta-Befehle und
ihrer Operanden findet sich in den Tabellen 4.7 und 4.7, ihre praktische Verwendung kann
man sich in der Datei t_olms4.asm ansehen.
Der Datenspeicher dieser 4-Bit-Controller besteht aus bis zu 128
Nibbles. Für die dafür benötigten sieben
Adreßbits war jedoch nur in den wenigsten Instruktionen Platz,
so daß einmal wieder Banking zur Adressierung herhalten
muß. Die meisten Befehle, die Speicher adressieren, enthalten
nur die untersten vier Bits der RAM-Adresse, und sofern nicht die
untersten 16 Nibbles angesprochen werden sollen, liefert das
P-Register die notwendigen obere Adreßbits. Dessen aktuellen
Wert teilt man dem Assembler über ein
Mit PAGE ist auch ein anderes Thema angeschnitten:
sowohl PAGE als auch SWITCH sind auf diesen
Controllern Maschinenbefehle, d.h. haben nicht ihre von anderen
Targets übliche Funktion. Der Pseudobefehl, um ein
SWITCH/CASE- Konstrukt einzuleiten, lautet im OLMS-50-Modus
SELECT, und die Seitengröße des Listings legt man
mit PAGESIZE fest.
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:
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[46] 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.
John Weinrich sei dank, habe ich nun auch die offiziellen
Datenblätter von Intel über diese 'Urväter' aller
Mikroprozessoren, und die Unklarheiten über die Syntax von
Registerpaaren (für 8-Bit-Operationen) sind fürs erste
ausgeräumt. Die Syntax lautet RnRm, wobei n
bzw. m gerade Integers im Bereich 0 bis E bzw. 1 bis F sind.
Dabei gilt immer m = n + 1.
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).
Wie schon weiter vorne erwähnt, ist es möglich, durch ein
Ähnlich wie beim Z80 oder 6502, sind auch beim 8085 die
undokumentierten Befehle nicht näher von Intel spezifiziert
worden, weshalb es nicht undenkbar ist, daß andere Assembler
andere Mnemonics dafür verwenden. Deshalb sollen auch diese
Befehle und ihre Funktion hier kurz aufgelistet werden. Und auch hier
wieder ist die Verwendung dieser Befehle auf eigenes Risiko - schon
der an sich zum 8085 aufwärtskompatible Z80 benutzt diese
Opcodes für völlig andere Funktionen...
Mit X5 ist dabei das ansonsten unbenutzte Bit 5 im PSW-Register
gemeint.
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:
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
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.
Eine kleine Besonderheit sind Befehle, die Atmel bereits in der
Architektur vorgesehen hat, aber noch in keinem Mitglied der Familie
implementiert wurden. Dabei handelt es sich um die Befehle MUL,
JMP und CALL. Besonders bei letzteren fragt man sich
vielleicht, wie man denn nun den 4 KWorte großen
Adreßraum des AT90S8515 erreichen kann, wenn die
'nächstbesten' Befehle RJMP und RCALL doch nur
2 KWorte weit springen kann. Der Kunstgriff lautet 'Abschneiden der
oberen Adreßbits' und ist näher bei der
WRAPMODE-Anweisung beschrieben.
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
Der Prozessorkern der Z8-Mikrokontroller beinhaltet keine eigenen
Register. Stattdessen kann ein 16er-Block des internen Adre"raums aus
RAM und I/O-Registern als 'Arbeitsregister' benutzt werden, die mit
4-Bit-Adressen angesprochen werden können. Welcher 16er-Block
als Arbeitsregister benutzt werden soll, wird mit dem RP-Register
festgelegt: Bits 4 bis 7 von RP definieren den 'Offset', der auf die
4-Bit-Arbeitsregisteradresse addiert wird, um eine 8-Bit-Adresse zu
erhalten.
Üblicherweise werden die Arbeitsregister in der Assemblersyntax
als Register R0...R15 angesprochen, man kann diese Arbeitsregister
aber auch als eine Methode zur effizienteren (kürzeren)
Adressierung eines 16er-Bocks im internen RAM betrachten.
Mit dem ASSUME-Befehl teilt man AS den aktuellen Wert von RP
mit. AS ist dann in der Lage, bei einer Adresse aus dem internen RAM
automatisch zu entscheiden, ob dieser Operand mit einer 4-Bit Adresse
angesprochen werden kann oder eine 8-Bit-Adresse verwendet werden
muß. Man kann diese Funktion auch dazu benutzen,
Arbeitsregistern symbolische Namen zu verpassen:
Beim eZ8 wird das Spielchen quasi eine Stufe weiter getrieben: der
interne Daten-Adreßbereich ist jetzt 12 statt 8 Bit groß.
Um kompatibel zum alten Z8-Kern zu sein, hat Zilog die
zusätzlichen Banking-Bits in den unteren vier Bits von RP
untergebracht - ein RP-Wert von 12h definiert also das
16er-Adreßfenster von 210h bis 21fh.
Die unteren vier Bits von RP definieren beim eZ8 gleichzeitig das
256er-Fenster, das man mit 8-Bit-Adressen erreichen kann - hier gilt
ein analoger Mechanismus, der dafür sorgt, daß AS
automatisch 12- oder 8-Bit-Adressen verwendet. 'Lange'
12-Bit-Adressen kann man mit zwei vorangestellten >-Zeichen
erzwingen.
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 hatte sich nach meinen Informationen leider
zwischenzeitlich auch dazu entschieden, diesen Prozessor ,,auf Eis''
zu legen, also auch kein Silizium geben. Das hatte natürlich zur
Folge, daß dieser Teil
...Schnitt, 20 Jahre später: auf einmal melden sich Leute bei
mir, daß Toshiba wohl doch TLCS-9000-Chips an Kunden verkauft
hat, und fragen nach den Unterlagen, weil sie Reverse-Engineering
betreiben. Vielleicht bekommen wir ja auf diesem Wege noch das eine
oder andere unklare Detail bestätigt oder geklärt. Fehler
in diesem Teil sind also weiterhin noch möglich und werden
natürlich bereinigt. Zumindest die Handvoll Beispiele in [120] werden aber richtig übersetzt.
Displacements im Maschinenbefehl selber können nur eine
bestimmte maximale Länge (z.B. 13 oder 9 Bit) haben. Ist das
Displacement länger, muß dem Befehl ein Präfix mit
den ''oberen Bits'' vorangestellt werden. AS wird solche Präfixe
automatisch nach Bedarf einsetzen, man kann jedoch auch mit einem dem
Displacement vorangestellten '>' das Setzen eines Präfix
erzwingen, z.B. so:
Toshiba hat seinerzeit für diesen Prozessor einen
(DOS-basierten) Assembler namens ASM31T geliefert. Dieser Assembler
unterstützt eine Reihe von Syntax-Elementen, die sich auf AS
nicht ohne Änderungen abbilden ließen, die die
Kompatibilität zu existierenden Quelldateien für andere
Targets gefährdert hätten. An folgenden Stellen werden
möglicherweise Änderungen erforderlich sein, um für
den ASM31T geschriebene Programme mit AS übersetzen zu
können:
Des weiteren fehlen AS im Moment die Fähigkeiten, auf
miteinander kollidierende Nutzungen von Funktionseinheiten in einem
Befehl hinzuweisen. Die Dokumentation von Toshiba ist an diesem Punkt
leider etwas schwer verständlich.
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.
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:
Ä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:
Ä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:
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:
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.16),
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.7 aufgelistet.
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:
In [89] 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:
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:
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:
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. Frühere
Versionen von AS implementierten diese Befehle über Makros in
der Datei REGMSP.INC. Wer diese Datei nicht einband, erhielt
bei über der Hälfte der insgesamt von TI definierten
Befehle Fehlermeldungen. Dies ist aktuell nicht mehr so, zusammen mit
der Erweiterung auf den CPU430X-Befehlssatz werden die Instruktionen
vom Assembler direkt implementiert. REGMSP.INC enthält
nur noch die Adressen von I/O-Registern. Wer aus irgendwelchen
Gründen die alten Makros braucht, findet sie jetzt in der
Datei EMULMSP.INC.
Die emulierten Instruktionen decken auch einige Sonderfälle ab,
die der TI-Assembler nicht beherrscht. So wird zum Beispiel
Der erste Mikrocontroller der Welt nun endlich auch in AS - lange hat
es gedauert, nun ist die Lücke geschlossen. Dieses Target hat
aber einige Tücken, die in diesem Abschnitt kurz angesprochen
werden sollen.
Zum einen ist der Befehlssatz dieser Controller teilweise über
die ROM-Maske veränderbar, d.h. man kann die Funktion einiger
Opcodes in Grenzen frei definieren. AS kennt nur die Befehle und
deren Kodierungen, die in [114] als
Default-Kodierungen beschrieben sind. Wer für eine spezielle
Anwendung andere Befehle bzw. gleiche Befehle mit anderem Opcode hat,
kann diese über Makros mit passende DB-Befehlen
ändern.
Des weiteren ist zu beachten, daß Sprünge und
Unterprogrammaufrufe nur die unteren 6 Bit der Zieladresse im Befehl
selber beinhalten. Die oberen 4 bzw. 5 Bits kommen aus Page- bzw.
Chapter-Registern, die vorher passend zu setzen sind. AS selber kann
hier nicht überprüfen, ob die Register vom Programmierer
korrekt gesetzt werden!. Zumindest für den Fall, daß man
im gleichen Chapter bleibt, gibt es die Assembler-Pseudobefehle
CALLL bzw. BL, die einen LDP- und einen
CALL/BR-Befehl zusammenfassen (was angesichts des kanppen
Programmspeichers eine bequeme, aber nicht immer effiziente Variante
ist).
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...
Original gab es für diese Reihe von DECT-Controllern mit relativ
einfachem Befehlssatz nur einen sehr schlichten Assembler von
National selber. Ein Assembler von IAR Systems ist angekündigt,
aber noch nicht erhältlich. Da die Entwicklungstools von IAR
allerdings auch nach Möglichkeit CPU-unabhängig angelegt
sind, kann man anhand erhältlicher Zielplattformen in
ungefähr abschätzen, wie dessen Pseudobefehle aussehen
werden, und damit im Blick sind die (wenigen) SC144xx-spezifisch
realisierten Befehle DC, DC8, DW16, DS, DS8, DS16, DW
angelegt. Bei Befehlen, die bereits im AS-Kern angelegt sind, wollte
ich natürlich nicht das Rad neu erfinden, deshalb hier eine
Tabelle mit Äquivalenzen:
Die Befehle ALIGN, END, ENDM, EXITM, MACRO, ORG, RADIX, SET
und REPT exisieren sowohl bei IAR als auch AS und haben
gleiche Bedeutung. Bei folgenden Befehlen muß man umstellen:
Keine direkte Entsprechung gibt es für die Befehle CASEON,
CASEOFF, LOCAL, LSTPAG, #undef und REPTI.
Ein direktes Äquivalent der Präprozessorbefehle ist
natürlich nicht möglich, solange AS keinen C-artigen
Präprozessor besitzt. C-artige Kommentare sind im Moment leider
auch nicht möglich. Achtung: Wer IAR-Codes für AS umsetzt,
muß die Präprozessorstatements nicht nur umwandeln,
sondern auch aus Spalte 1 herausbewegen, da bei AS in Spalte 1 nur
Labels stehen dürfen!
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:
Analog wie beim 78K0 benutzt NEC auch hier wieder Dollar- und
Ausrufezeichen für verschiedene Längen von
Adreßsausdrücken. Zwischen langen und kurzen Adressen
(sowohl im RAM- als auch SFR-Bereich) wird wieder automatisch
entschieden, nur relative Adressierung muß man manuell
anwählen, wenn ein Befehl beides unterstützt (z.B.
BR).
Noch eine Anmerkung (die im übrigens auch für den 78K0
gilt): Wer mittels RELAXED mit Motorola-Syntax arbeitet,
muß Hexadezimalkonstanten klammern, weil das führende
Dollarzeichen u.U. als relative Adressierung mißverstanden
wird...
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!
Darauf, daß man bei Anwendungen mit mehr als 64K ROM oder 64K
RAM darauf achten sollte, AS die korrekte momentane Belegung der
Bank-Register mitzuteilen, wurde bereits in Zusammenhang mit dem
ASSUME-Befehl erwähnt. AS überprüft bei jedem
absoluten Zugriff anhand dieser Annahmen, ob evtl. ein Zugriff auf
eine Speicherstelle erfolgt, die momentan überhaupt nicht
greifbar ist. Standardmäßig sind dafür natürlich
nur DTB und DPR wichtig, denn ADB bzw. SSB/USB werden nur bei
indirekten Zugriffen über RW2/RW6 bzw. RW3/RW7 benutzt, und bei
indirekten Zugriffen greift diese Prüfmimik ohnehin nicht. Nun
ist es aber so, daß man - ähnlich wie beim 8086 - einem
Befehl eine Segmentpräfix voranstellen kann, mit dem DTB
für den folgenden Befehl durch ein beliebiges anderes Register
ersetzt werden kann. AS führt deswegen über die verwendeten
Präfixbefehle Buch und schaltet bei der Prüfung für
den nächsten Prozessorbefehl um - eine zwischen dem
Segmentpräfix und Prozessorbefehl eingestreute Pseudoanweisung
löscht den Merker also nicht. Dies gilt auch für
Pseudobefehle zur Datenablage oder Veränderung des
Programmzählers - aber wer macht so etwas schon ;-)
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 Tabellen 5.1 und 5.2).
Das Segment-Feld gibt an, in welchen Adreßraum des Prozessors
der folgende Code gehört. Dabei gilt die in Tabelle 5.3 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 hintangestellten h, einem
voranstehenden Dollarzeichen oder 0x wie in C versieht (z.B.
$10, 10h oder 0x10 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. Die
0x-Variante ist hier sicherlich angenehmer.
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:
Normalerweise benutzt das AVR-Format immer eine Adreßlänge
von 3 Bytes. Manche Programme mögen das leider nicht...deshalb
kann man mit dem Schalter
Das Mico8-Format unterscheidet sich insofern aus den anderen
Formaten, als daß es keine Adreßfelder besitzt - es ist
eine schlichte Auflistung der Instruktionswörter im
Programmspeicher. Bei der Benutzung muß darauf geachtet werden,
daß der belegte Adreßbereich (der sich z.B. mit PLIST
anzeigen läßt) bei Null beginnt und fortlaufend ist.
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
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 Sonderwert für Start-und Endadresse beim r-Parameter ist ein
schlichtes Dollar-Zeichen ($) bzw. ein '0x' erlaubt. Dies
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
Für das DSK- sowie Intel- und Motorola-Format relevant ist
dagegen die Option
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, Marco Awater, Len Bayles, Andreas
Bolsch, Rolf Buchholz, Bernd Casimir, Nils Eilers, Gunther Ewald,
Stephan Hruschka, Peter Kliegelhöfer, Ulf Meinke, Matthias Paul,
Norbert Rosch, Steffen Schmid, Leonhard Schneider, Ernst Schwab,
Michael Schwingen, Oliver Sellke, Christian Stelter, Patrik
Strömdahl, Oliver Thamm, Thorsten Thiele, Andreas Wassatsch,
John Weinrich.
...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.
Ursprünglich war AS ein in Turbo-Pascal geschriebenes Programm.
Für diese Entscheidung gab es Ende der 80er Jahre eine Reihe von
Gründen: Zum einen war ich damit wesentlich vertrauter als mit
jedem C-Compiler, zum anderen waren alle C-Compiler unter DOS
verglichen mit der IDE von Turbo-Pascal ziemliche Schnecken. Anfang
1997 zeichnete sich jedoch ab, daß sich das Blatt gewendet
hatte: Zum einen hatte Borland beschlossen, die DOS-Entwickler im
Stich zu lassen (nochmals ausdrücklich keinen schönen Dank,
Ihr Pappnasen von Borland!), und Version 7.0 etwas namens 'Delphi'
nachfolgen ließen, was zwar wohl wunderbar für
Windows-Programme geeignet ist, die zu 90% aus Oberfläche und
zufällig auch ein bißchen Funktion bestehen, für
kommandozeilenorientierte Progamme wie AS aber reichlich unbrauchbar
ist. Zum anderen hatte sich bereits vor diesem Zeitpunkt mein
betriebssystemmäßiger Schwerpunkt deutlich in Richtung
Unix verschoben, und auf ein Borland-Pascal für Linux hätte
ich wohl beliebig lange warten können (an alle die, die jetzt
sagen, Borland würde ja an soetwas neuerdings basteln: Leute,
das ist Vapourware, und glaubt den Firmen nichts, solange Ihr nicht
wirklich in den Laden gehen und es kaufen könnt!). Von daher war
also klar, daß der Weg in Richtung C gehen mußte.
Nach der Erfahrung, wohin die Verwendung von Inselsystemen
führt, habe ich bei der Umsetzung auf C Wert auf eine
möglichst große Portabilität gelegt; da AS jedoch
z.B. Binärdateien in einem bestimmten Format erzeugen muß
und an einigen Stellen betriebssystemspezifische Funktionen nutzt,
gibt es einige Stellen, an denen man anpassen muß, wenn man AS
zum ersten Mal auf einer neuen Plattform übersetzt.
AS ist auf einen C-Compiler ausgelegt, der dem ANSI-Standard
entspricht; C++ ist ausdrücklich nicht erforderlich. Wenn Sie
nur einen Compiler nach dem veralteten Kernighan&Ritchie-Standard
besitzen, sollten Sie sich nach einem neuen Compiler umsehen; der
ANSI-Standard ist seit 1989 verabschiedet und für jede aktuelle
Plattform sollte ein ANSI-Compiler verfügbar sein, zur Not,
indem man mit dem alten Compiler GNU-C baut. Im Quellcode sind zwar
einige Schalter vorhanden, um den Code K&R-näher zu machen,
aber dies ist ein nicht offiziell unterstütztes Feature, das ich
nur intern für ein ziemlich antikes Unix benutze. Alles weitere
zum 'Thema K&R' steht in der Datei README.KR.
Der Sourcenbaum ist durch einige in der Pascal-Version nicht
vorhandene Features (z.B. dynamisch ladbare Nachrichtendateien,
Testsuite, automatische Generierung der Dokumentation aus einem
Quellformat) deutlich komplizierter geworden. Ich werde versuchen,
die Sache Schritt für Schritt aufzudröseln:
Wie ich schon andeutete, ist AS (glaube ich jedenfalls...) auf
Plattformunabhängigkeit und leichte Portierbarkeit getrimmt.
Dies bedeutet, daß man die Plattformunabhängigkeiten in
möglichst wenige Dateien zusammenzieht. Auf diese Dateien werde
ich im folgenden eingehen, und dieser Abschnitt steht ganz vorne,
weil es sicher eines der wichtigsten ist:
Die Generierung aller Komponenten von AS erfolgt über ein
zentrales Makefile. Damit dies funktioniert, muß man
ihm ein passendes Makefile.def anbieten, das die
plattformabhängigen Einstellungen wie z.B. Compilerflags
vorgibt. Im Unterverzeichnis Makefile.def-samples finden
sich eine Reihe von Includes, die für gängige Plattformen
funktionieren (aber nicht zwangsweise optimal sein müssen...).
Wenn die von Ihnen benutzte Plattform nicht dabei ist, können
Sie die Beispieldatei Makefile.def.tmpl als Ausgangspunkt
verwenden (und das Ergebnis mir zukommen lassen!).
Ein weiterer Anlaufpunkt zum Abfangen von Systemabhängigkeiten
ist die Datei sysdefs.h. Praktisch alle Compiler definieren
eine Reihe von Präprozessorsymbolen vor, die den benutzten
Zielprozessor sowie das benutzte Betriebsystem beschreiben. Auf einer
Sun Sparc unter Solaris mit den GNU-Compiler sind dies z.B. die
Symbole __sparc und __SVR4. In sysdefs.h werden diese
Symbole genutzt, um für die restlichen, systemunabhängigen
Dateien eine einheitliche Ungebung bereitzustellen. Insbesondere
betrifft dies Integer-Datentypen einer bekannten Länge, es kann
aber auch die Nach- oder Redefinition von C-Funktionen betreffen, die
auf einer bestimmten Plattform nicht oder nicht
standardgemäß vorhanden sind. Was da so an Sachen
anfällt, liest man am besten selber nach. Generell sind die
#ifdef-Statements in zwei Ebenen gegliedert: Zuerst wird eine
bestimmte Prozessorplattform ausgewählt, dann werden in diesem
Abschnitt die Betriebssysteme auseinandersortiert.
Wenn Sie AS auf eine neue Plattform portieren, müssen Sie zwei
für diese Plattform typische Symbole finden und
sysdefs.h passend erweitern (und wieder bin ich an dem Ergebnis
interessiert...).
...stellen den gößten Teil aller Module dar. Alle
Funktionen im Detail zu beschreiben, würde den Rahmen dieser
Beschreibung sprengen (wer hier mehr wissen will, steigt am besten
selbst in das Studium der Quellen ein, so katastrophal ist mein
Programmierstil nun auch wieder nicht...), deshalb hier nur eine
kurze Auflistung, welche Module vorhanden sind und was für
Funktionen sie beinhalten:
Diese Datei ist die Wurzel von AS: Sie enthält die
main()-Funktion von AS, die Verarbeitung aller
Kommandozeilenoptionen, die übergeordnete Steuerung aller
Durchläufe durch die Quelldateien sowie Teile des
Makroprozessors.
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.
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.
Optional kann AS Debug-Informationen für andere Tools wie
Simulatoren oder Debugger erzeugen, die einen Rückbezug auf den
Quellcode erlauben, in diesem Modul gesammelt und nach Ende der
Assemblierung in einem von mehreren Formaten ausgegeben werden
können.
Dieses Modul enthält lediglich Deklarationen von überall
benötigten Konstanten und gemeinsam benutzten Variablen.
Intern vergibt AS für jede benutzte Quelldatei eine fortlaufende
Nummer, die zur schnellen Referenzierung benutzt wird. Die Vergabe
dieser Nummern und die Umwandlung zwischen Nummer und Dateinamen
passiert hier.
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 ist die Listenstruktur definiert, über die AS
die Verschachtelung von Include-Dateien im Listing ausgeben kann.
Wenn man in einer Code-Zeile das benutzende Mnemonic ermitteln will,
ist das einfache Durchvergleichen mit allen vorhandenen Befehlen (wie
es noch in vielen Codegeneratoren aus Einfachheit und Faulheit
passiert) nicht unbedingt die effizienteste Variante. In diesem Modul
sind zwei verbesserte Strukturen (Binärbaum und Hash-Tabelle)
definiert, die eine effizientere Suche ermöglichen und die
einfache lineare Suche nach und nach ablösen
sollen...Priorität nach Bedarf...
In diesem Modul finden sich die Routinen zur Speicherung und Abfrage
von Makros. Der eigentliche Makroprozessor befindet sich (wie bereits
erwähnt) in as.c.
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.
Hier finden sich gesammelt einige häufig gebrauchte
Unterroutinen, welche in erster Linie die Bereiche Fehlerbehandlung
und 'gehobene' Stringverarbeitung abdecken.
Wie am Anfang erwähnt, war AS ursprünglich ein in
Borland-Pascal geschriebenes Programm. Bei einigen intrinsischen
Funktionen des Compilers war es einfacher, diese zu emulieren,
anstatt alle betroffenen Stelle im Quellcode zu ändern. Na ja...
Dieses Modul definiert einen Datentyp, mit dem eine Liste von
Adreßbereichen verwaltet werden kann. Diese Funktion wird von
AS für die Belegungslisten benötigt, außerdem
benutzten P2BIN und P2HEX diese Listen, um vor Überlappungen zu
warnen.
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:
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.
Aus Speicherersparnisgründen sind hier einige von diversen
Codegeneratoren benutzen Variablen gesammelt.
Doch noch ein bißchen Maschinenabhängigkeit, jedoch ein
Teil, um den man sich nicht zu kümmern braucht: Ob eine Maschine
Little- oder Big-Endianess benutzt, wird in diesem Modul beim
Programmstart automatisch bestimmt. Weiterhin wird geprüft, ob
die in sysdefs.h gemachten Typfestlegungen für
Integervariablen auch wirklich die korrekten Längen ergeben.
Gesammelt sind hier alle von AS unterstützten
Zielprozessorfamilien, die dafür in Code-Dateien verwendeten
Kennzahlen (siehe Kapitel 5.1)
sowie das von P2HEX defaultmäßig zu verwendende
Ausgabeformat. Ziel dieser Tabelle ist es, Das Hinzufügen eines
neuen Prozessors möglichst zu zentralisieren, d.h. es sind im
Gegensatz zu früher keine weiteren Modifikationen an den Quellen
der Hilfsprogramme mehr erforderlich.
Hier ist die Umwandlung von Fehlernummern in Klartextmeldungen
abgelegt. Hoffentlich treffe ich nie auf ein System, auf dem die
Nummern nicht als Makros definiert sind, dann kann ich nämlich
dieses Modul komplett umschreiben...
Die C-Version von AS liest alle Meldungen zur Laufzeit aus Dateien,
nachdem die zu benutzende Sprache ermittelt wurde. Das Format der
Nachrichtendateien ist kein einfaches, sondern ein spezielles,
kompaktes, vorindiziertes Format, das zur Übersetzungszeit von
einem Programm namens 'rescomp' (dazu kommen wir noch) erzeugt wird.
Dieses Modul ist das Gegenstück zu rescomp, die den korrekten
Sprachenanteil einer Datei in ein Zeichenfeld einliest und
Zugriffsfunktionen anbietet.
In diesem Modul wird ermittelt, welche nationalen Einstellungen
(Datums- und Zeitformat, Ländercode) zur Laufzeit vorliegen. Das
ist leider eine hochgradig systemspezifische Sache, und momentan sind
nur drei Methoden definiert: Die von MS-DOS, die von OS/2 und die
typische Unix-Methode über die locale-Funktionen. Für alle
anderen Systeme ist leider NO_NLS angesagt...
Zum einen ist hier eine spezielle open-Funktion gelandet, die die
Sonderstrings !0...!2 als Dateinamen kennt und dafür
Duplikate der Standard-Dateihandles stdin, stdout und stderr erzeugt,
zum anderen wird hier festgestellt, ob die Standardausgabe auf ein
Gerät oder eine Datei umgeleitet wurde. Das bedingt auf
nicht-Unix-Systemen leider auch einige Speziallösungen.
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.
Die momentan gültige Version ist für AS und alle anderen
Hilfsprogramme hier zentral gespeichert.
Dies Module bilden den Hauptteil der AS-Quellen: jedes Modul
beinhaltet den Codegenerator für eine bestimmte
Prozessorfamilie.
Ein kleines Modul zur Umwandlung von Integerzahlen in
Hexadezimaldarstellung. In C nicht mehr unbedingt erforderlich
(außer zur Wandlung von long long-Variablen, was leider nicht
alle printf()'s unterstützen), aber es ist im Rahmen
der Portierung eben auch stehengeblieben.
Die Quellen von P2BIN.
Die Quellen von P2HEX.
Die Quellen von BIND.
Die Quellen von PLIST.
Hier sind gesammelt die Unterroutinen, die von allen Hilfsprogrammen
benötigt werden, z.B. für das Lesen von Code-Dateien.
Dies ist ein Minimalfilter, das ANSI-C-Files in Kernighan-Ritchie
umwandelt. Um es genau zu sagen: es werden nur die
Funktionsköpfe umgewandelt, und auch nur dann, wenn sie
ungefähr so formatiert sind, wie es mein Schreibstil eben ist.
Es komme also keiner auf die Idee, das wäre ein universeller
C-Parser!
Ein kleiner Filter, der bei der Installation auf DOS- oder
OS/2-Systemen gebraucht wird. Da DOS und OS/2 den Zeilenvorschub mit
CR/LF vornehmen, Unix-Systeme jedoch nur mit LF, werden
sämtliche mitgelieferten Assembler-Includes bei der Installation
durch diesen Filter geschickt.
Für DOS und OS/2 übernimmt dieses Modul die Funktion die
Funktion des cmp-Befehls, d.h. den binären Vergleich von Dateien
während des Testlaufes. Während dies prinzipiell auch mit
dem mitgelieferten comp möglich wäre, hat bincmp keine
lästigen interaktiven Abfragen (bei denen man erst einmal
herausfinden muß, wie man sie auf allen Betriebssystemversionen
abstellt...)
Dies ist das Untermodul in tex2doc, daß für die
Silbentrennung von Worten sorgt. Der verwendete Algorithmus is
schamlos von TeX abgekupfert.
Die Definition der Silbentrennungsregeln für die deutsche
Sprache.
Dies ist der 'Resourcencompiler' von AS, d.h. das Werkzeug, das die
lesbaren Dateien mit Stringresourcen in ein schnelles, indiziertes
Format umsetzt.
Ein Werkzeug, daß die LaTeX-Dokumentation von AS in ein
ASCII-Format umsetzt.
Ein Werkzeug, daß die LaTeX-Dokumentation von AS in ein
HTML-Dokument umsetzt.
Diese Progrämmchen besorgen die Wandlung zwischen
Sonderzeichenkodierung im ISO-Format (alle AS-Dateien verwenden im
Auslieferungszustand die ISO8859-1-Kodierung für Sonderzeichen)
und Sonderzeichenkodierung im systemspezifischen Format. Neben einer
Plain-ASCII7-Variante sind dies im Augenblick die
IBM-Zeichensätze 437 und 850.
Die Definition der Silbentrennungsregeln für die englische
Sprache.
Wie bereits erwähnt, verwendet der C-Quellenbaum von AS ein
dynamisches Ladeverfahren für alle (Fehler-)Meldungen.
Gegenüber den Pascal-Quellen, in denen alle Meldungen in einem
Include-File gebündelt waren und so in die Programme
hineinübersetzt wurden, macht es dieses Verfahren
überflüssig, mehrere sprachliche Varianten von AS zur
Verfügung zu stellen: es gibt nur noch eine Version, die beim
Programmstart die zu benutzende Variante ermittelt und aus den
Nachrichtendateien die entsprechende Komponente lädt. Kurz zur
Erinnerung: Unter DOS und OS/2 wird dazu die gewählte
COUNTRY-Einstellung zu Rate gezogen, unter Unix werden die
Environment-Variablen LC_MESSAGES, LC_ALL und LANG
befragt.
Eine Quelldatei für den Message-Compiler rescomp hat
üblicherweise die Endung .res. Der Message-Compiler
erzeugt aus dieser Datei ein oder zwei Dateien:
Die Quelldatei für den Message-Compiler ist eine reine
ASCII-Datei, also mit jedem beliebigen Editor bearbeitbar, und
besteht aus einer Reihe von Steueranweisungen mit Parametern.
Leerzeilen sowie Zeilen, die mit einem Semikolon beginnen, werden
ignoriert. Das Inkludieren anderer Dateien ist über das
Include-Statement möglich:
Am Anfang jeder Quelldatei müssen zwei Statements stehen, die
die im folgenden definierten Sprachen beschreiben. Das wichtigere der
beiden Statements ist Langs, z.B.:
In einer Quellcodedistribution von AS ist diese Dokumentation nur als
LaTeX-Dokument enthalten. Andere Formate werden aus dieser mit Hilfe
von mitgelieferten Werkzeugen automatisch erzeugt. Zum einen
reduziert dies den Umfang einer Quellcodedistribution, zum anderen
müssen Änderungen nicht an allen Formatversionen eines
Dokumentes parallel vorgenommen werden, mit all den Gefahren von
Inkonsistenzen.
Als Quellformat wurde LaTeX verwendet, weil...weil...weil es eben
schon immer vorhanden war. Zudem ist TeX fast beliebig portierbar und
paßt damit recht gut zum Anspruch von AS. Eine
Standard-Distribution erlaubt damit eine 'ordentliche' Ausgabe auf so
ziemlich jedem Drucker; für eine Konvertierung in die
früher immer vorhandene ASCII-Version liegt der Konverter
tex2doc bei; zusätzlich einen Konverter tex2html, so daß
man die Anleitung direkt ins Internet stellen kann.
Die Erzeugung der Dokumentation wird mit einem schlichten
Da AS mit binären Daten von genau vorgegebener Struktur umgeht,
ist er naturgemäß etwas empfindlich für System- und
Compilerabhängigkeiten. Um wenigstens eine gewisse Sicherheit zu
geben, daß alles korrekt durchgelaufen ist, liegt dem Assembler
im Unterverzeichnis tests eine Menge von
Test-Assemblerquellen bei, mit denen man den frisch gebauten
Assembler testen kann. Diese Testprogramme sind in erster Linie
darauf getrimmt, Fehler in der Umsetzung des Maschinenbefehlssatzes
zu finden, die besonders gern bei variierenden Wortlängen
auftreten. Maschinenunabhängige Features wie der Makroprozessor
oder bedingte Assemblierung werden eher beiläufig getestet, weil
ich davon ausgehe, daß sie überall funktionieren, wenn sie
bei mir funktionieren...
Der Testlauf wird mit einem einfachen make test angestoßen.
Jedes Testprogramm wird assembliert, in eine Binärdatei
gewandelt und mit einem Referenz-Image verglichen. Ein Test gilt als
bestanden, wenn Referenz und die neu erzeugte Datei Bit für Bit
identisch sind. Am Ende wird summarisch die Assemblierungszeit
für jeden Test ausgegeben (wer will, kann mit diesen Ergebnissen
die Datei BENCHES ergänzen), zusammen mit dem Erfolg
oder Mißerfolg. Jedem Fehler ist auf den Grund zu gehen, selbst
wenn er bei einem Zielprozessor auftritt, den Sie nie nutzen werden!
Es ist immer möglich, daß dies auf einen Fehler hinweist,
der auch bei anderen Zielprozessoren auftritt, nur zufällig
nicht in den Testfällen.
Der mit Abstand häufigste Grund, im Quellcode von AS etwas zu
verändern, dürfte wohl die Erweiterung um einen neuen
Zielprozessor sein. Neben der Ergänzung der Makefiles um das
neue Modul ist lediglich eine Modifikation der Quellen an wenigen
Stellen erforderlich, den Rest erledigt das neue Modul, indem es sich
in der Liste der Codegeneratoren registriert. 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:
Der erste Schritt der Registrierung ist die Eintragung des Prozessors
oder der Prozessorfamilie in der Datei headids.c. Wie
bereits erwähnt, wird diese Datei von den Hilfsprogrammen
mitbenutzt und spezifiziert die einer Prozessorfamilie zugeordnete
Kenn-ID in Codedateien sowie das zu verwendende Hex-Format. Bei der
Wahl der Kenn-ID würde ich mir etwas Absprache wünschen...
Das Modul, das für den neuen Prozessor zuständig sein soll,
sollte einer gewissen Einheitlichkeit wegen den Namen
code.... tragen, wobei ..... etwas mit dem
Prozessornamen zu tun haben sollte. Den Kopf mit den Includes
übernimmt man am besten direkt aus einem bereits vorhandenen
Codegenerator.
Mit Ausnahme einer Initialisierungsfunktion, die zu Anfang der
main()-Funktion im Modul as.c aufgerufen werden
muß, braucht das neue Modul keinerlei Funktionen oder Variablen
zu exportieren, da die ganze Kommunikation zur Laufzeit über
indirekte Sprünge abgewickelt wird. Die dazu erforderlichen
Registrierungen müssen in der Initialisierungsfunktion des
Moduls 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 müssen noch einige Funktionszeiger
besetzt wird, mit denen der Codegenerator sich in AS einbindet:
Optional kann ein Codegenerator auch noch folgende weitere
Funktionszeiger besetzen:
Wer will, kann sich übrigens auch mit einem Copyright-Eintrag
verewigen, indem er in der Initialisierung des Moduls (bei den
AddCPU-Befehlen) einen Aufruf der Prozedur AddCopyright
einfügt, in der folgenden Art:
Bei Bedarf kann sich das Modul 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 Funktion
AddInitPassProc angebotene Haken bietet die Möglichkeit,
derartige Initialisierungen vorzunehmen. Die übergebene
Callback-Funktion wird vor Beginn eines Durchgangs aufgerufen.
Analog zu AddInitPassProc funktioniert die über
AddCleanUpProc aufgebaute Funktionsliste, 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.
Nach diesen Präliminarien 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.c und asmpars.c. Ich kann hier nur
einige generelle Hinweise geben:
Eine winzige Änderung ist auch noch an den Quellen der
Dienstprogramme nötig, und zwar in der Routine
Granularity() in toolutils.c: Falls eines der
Adreßräume dieses Prozessors eine andere Granularität
als 1 hat, muß dort die Abfrage passend ergänzt werden,
sonst verzählen sich PLIST, P2BIN und P2HEX...
Sie haben Interesse an diesem Thema? Wunderbar! Das ist eine Sache,
die von Programmierern gerne außen vor gelassen wird,
insbesondere, wenn sie aus dem Land der unbegrenzten
Möglichkeiten kommen...
Die Lokalisierung auf eine neue Sprache gliedert sich in zwei Teile:
Die Anpassung der Programmmeldungen sowie die Übersetzung der
Anleitung. Letzteres ist sicherlich eine Aufgabe herkulischen
Ausmaßes, aber die Anpassung der Programmeldungen solle in ein
bis zwei Nachmittagen über die Bühne zu bekommen sein, wenn
man sowohl die neue als auch eine der bisher vorhandenen Sprachen gut
kennt. Leider ist die Übersetzung auch nichts, was man
Stück für Stück machen kann, denn der
Ressourcencompiler kann im Moment nicht mit einer variablen Zahl von
Sprachen in den verschiedenen Meldungen umgehen, es heißt also
'alles oder nichts'.
Als erstes ergänzt man in header.res die neue Sprache.
Die für die Sprache passende zweibuchstabige Abkürzung holt
man sich vom nächsten Unix-System (wenn man nicht ohnehin darauf
arbeitet...), die internationale Vorwahl aus dem nächsten
DOS-Handbuch.
Im zweiten Schritt geht man jetzt durch alle anderen
.res-Dateien und ergänzt die Message-Statements.
Nocheinmal sei darauf hingewiesen, Sonderzeichen in der HTML-artigen
Schreibweise und nicht direkt einzusetzen!
Wenn dies geschafft ist, kann man mit einem make alle betroffenen
Teile neu bauen und erhält danach einen Assembler, der eine
Sprache mehr schickt. Bitte nicht vergessen, die Ergebnisse an mich
weiterzuleiten, damit mit der nächsten Release alle etwas davon
haben :-)
o) 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.
p) MELPS4500
q) M16
r) M16
s) 4004 -> 4040
Der 4040 besitzt gegenüber seinem Vorgänger ein gutes
Dutzend zusätzlicher Maschineninstruktionen.
t) 4008 -> 8008NEW
Intel hat 1975 die Mnemonics des umdefiniert, die zweite Variante
spiegelt diesen neuen Befehlssatz wieder. Eine gleichzeitige
Unterstützung beider Varianten war nicht möglich, da
teilweise Überschneidungen vorliegen.
u) 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. Den 80C390
könnte man vielleicht als die 'kleine Lösung' bezeichnen:
Dallas Semiconductor hat den Befehlssatz und die Architektur nur so
weit verändert, wie es für die 16 MByte großen
Adreßräume notwendig war.
v) 87C750 -> 8051, 8052, 80C320, 80C501, 80C502, 80C504, 80515, and 80517 -> 80C390 -> 80C251
w) 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.
x) 8080 -> 8085 -> 8085UNDOC
Der 8085 kennt zusätzlich die Befehle RIM und
SIM zum Steuern der Interruptmaske und der zwei I/O-Pins. Der
Typ 8085UNDOC schaltet zusätzliche, nicht von Intel
dokumentierte Befehle ein. Diese Befehle sind in Abschnitt 4.19 dokumentiert.
y) 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.
z) 80960
aa) 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.
ab) XAG1, XAG2, XAG3
Diese Prozessoren unterscheiden sich nur in der Größe des
eingebauten ROMs, die in STDDEFXA.INC definiert ist.
ac) AT90S1200 -> AT90S2313 -> AT90S4414 -> AT90S8515 -> ATMEGA8
-> ATMEGA16
Der erste Vertreter der AVR-Reihe stellt die Minimalkonfiguration
dar, ohne RAM-Speicher und demzufolge auch ohne Load/Store-Befehle.
Die nächsten drei Prozessoren unterscheiden sich nur im
Speicherausbau und in der eingebauten Peripherie, was in
REGAVR.INC differenziert wird. Ähnlich verhält es sich
mit den Mega-AVRs, die gegenüber den Vorgängern aber auch
neue Maschinenbefehle mitbringen.
ad) 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.
ae) 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.
af) 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.
ag) Z8601, Z8604, Z8608, Z8630, Z8631 -> eZ8
Die Varianten mit Z8-Kern unterscheiden sich nur in Speicherausbau
und Peripherie, d.h. die Wahl hat auf den unterstützten
Befehlssatz keinen Effekt. Deutlich anders ist jedoch der eZ8, mit
einem stark erweiterten Befehlssatz, der auch nur auf Quellebene
weitestgehens aufwärts-kompatibel ist.
ah) KCPSM, KCPSM3
Bei beiden Prozessorkernen handelt es sich um keine
eigenständigen Bausteine, sondern Logik-Kerne für
Gate-Arrays der Firma Xilinx. Die 3er-Variante bietet einen
größeren Adreßraum sowie einige zusätzliche
Instruktionen. Es ist zu beachten, daß sie nicht binär
aufwärtskompatibel ist!
ai) MICO8_05, MICO8_V3, MICO8_V31
Leider hat Lattice die Maschinencodes des Mico8 mehrfach
geändert, so daß verschiedene Targets notwendig wurden, um
auch alte Designs weiter zu unterstützen. Die erste Variante
entspricht der Variante, wie sie im 2005er-Manual beschrieben wurde,
die beiden anderen die Versionen 3.0 bzw. 3.1.
aj) 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.27 genauer beleuchtet.
ak) 90C141
al) 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.
am) 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.
an) 97C241
ao) TC9331
ap) 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.
aq) 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.
ar) 17C42
as) 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).
at) ST7
au) 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.
av) 6804
aw) 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.
ax) 320C25 -> 320C26 -> 320C28
Diese Prozessoren unterscheiden sich nur leicht in der
On-Chip-Peripherie sowie den Konfigurationsbefehlen.
ay) 320C30, 320C31 -> 320C40, 320C44
Der 320C31 ist eine etwas ,,abgespeckte'' Version des 320C30 mit dem
gleichen Befehlssatz, jedoch weniger Peripherie. In STDDEF3X.INC wird
diese Unterscheidung ausgenutzt. Die C4x-Varianten sind
Quellcode-aufwärtskompatibel, unterscheiden sich im
Maschinencode einiger Befehle jedoch subtil. Auch hier ist ist der
C44 eine abgespeckte Version des C40, mit weniger Peripherie und
kleinerem Adre"raum.
az) 320C203 -> 320C50, 320C51, 320C53
Ersterer ist der generelle Repräsentant für die
C20x-Signalprozessorfamilie, die eine Untermenge des
C5x-Befehlssatzes implementieren. Die Unterscheidung zwischen den
verschiedenen C5x-Prozessoren wird von AS momentan nicht ausgenutzt.
ba) 320C541
Dies ist momentan der Pepräsentant für die
TMS320C54x-Familie...
bb) 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.
bc) TMS70C00, TMS70C20, TMS70C40, TMS70CT20, TMS70CT40, TMS70C02, TMS70C42, TMS70C82, TMS70C08, TMS70C48
bd) 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.
be) MSP430 -> MSP430X Die X-Variante des CPU-Kerns erweitert den
Adreßraum von 64 KiByte auf 1 MiByte und erweitert den
Befehlssatz, um Instrutionen mehrfach ausführen zu
können.
bf) TMS1000, TMS1100, TMS1200, TMS1300
Für TMS1000 und TMS1200 sind jeweils 1 KByte ROM und 64 Nibbles
RAM vorgesehen, für TMS1100 und TMS1300 jeweils das doppelte.
Des weiteren hat TI für TMS1100 und TMS1300 einen deutlich
anderen Dewfault-Befehlssatz vorgesehen (AS kennt nur die Default-
Befehlssätze!).
bg) SC/MP
bh) 8070
Dieser Prozessor repräsentiert die gesamte 807x-Familie (die
mindestens aus den 8070, 8072 und 8073 besteht), der jedoch ein
einheitlicher CPU-Kern gemeinsam ist.
bi) 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...
bj) COP410 -> COP420 -> COP440 -> COP444 Die COP42x-Derivate
bieten einige weitere Befehle, des weiteren wurden Befehlen in
ihrem Wertebereich erweitert.
Diese Gruppe von DECT-Controller unterscheidet sich in ihrem
Befehlsumfang, da jeweils unterschiedliche B-Feld Datenformate
unterstützt werden und deren Architektur im Laufe der Zeit
optimiert wurde.
bk) SC14400, SC14401, SC14402, SC14404, SC14405, SC14420, SC14421, SC14422, SC14424
bl) 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!
bm) 7566 <-> 7508
Es existieren in der µPD75xx-Familie zwei verschiedene
CPU-Kerne: Der 7566 repräsentiert den 'instruction set B', der
deutlich weniger Befehle, einige Register weniger und kleinere
Adreßräume erlaubt. Der 7508 repräsentiert den
'vollen' Befehlssatz A. VORSICHT! Beide
Maschinen-Befehlssätze sind nicht 100%-ig binärkompatibel!
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.
bn) 75402, 75004, 75006, 75008, 75268, 75304, 75306, 75308, 75312, 75316, 75328, 75104, 75106, 75108, 75112, 75116, 75206, 75208, 75212, 75216, 75512, 75516
bo) 78070
Dies ist das einzige, mir momentan vertraute Mitglied der
78K0-Familie von NEC. Es gelten ähnliche Aussagen wie zur
COP8-Familie!
bp) 78214
Dies ist momentan der Repräsentant der 78K2-Familie von NEC.
bq) 78310
Dies ist momentan der Repräsentant der 78K3-Familie von NEC.
br) 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!
bs) 77230
Die einfacheren Mitglieder dieser Familie von SCSI-Prozessoren
besitzen einige Befehlsvarianten nicht, außerdem unterscheiden
sie sich in ihrem Satz interner Register.
bt) SYM53C810, SYM53C860, SYM53C815, SYM53C825, SYM53C875, SYM53C895
bu) MB89190
Dieser Prozessortyp repräsentiert die F2MC8L-Serie
von Fujitsu...
bv) MB9500
...so wie dieser es momentan für die 16-Bit-Varianten von
Fujitsu tut!
bw) MSM5840, MSM5842, MSM58421, MSM58422, MSM5847
Diese Varianten der OLMS-40-Familie unterscheiden sich im Befehlssatz
sowie im internen Programm- und Datenspeicher.
bx) MSM5054, MSM5055, MSM5056, MSM6051, MSM6052
Gleiches wie bei der OLMS-40-Familie: Unterschiede im Befehlssatz
sowie im internen Programm- und Datenspeicher.
by) 1802 -> 1804, 1805, 1806 -> 1804A, 1805A 1806A
1804, 1805 und 1806 haben gegenüber dem 'Original' 1802 einen
leicht erweiterten Befehlssatz sowie on-chip-RAM und einen
integrierten Timer. Die A-Versionen erweitern den Befehlssatz um
DSAV, DBNZ, sowie um Befehle für Addition und
Subtraktion im BCD-Format.
bz) XS1
Dieser Typ repräsentiert die XCore-''Familie''.
ca) 1750
MIL STD 1750 ist ein Standard, also gibt es auch nur eine
(Standard-)Variante...
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.
3.2.4. SUPMODE, FPU, PMMU
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.
3.2.5. FULLPMMU
3.2.6. PADDING
3.2.7. PACKING
3.2.8. MAXMODE
3.2.9. EXTMODE und LWORDMODE
3.2.10. SRCMODE
3.2.11. BIGENDIAN
3.2.12. WRAPMODE
3.2.13. SEGMENT
Zu Adreßbereich und Initialwerten der Segmente siehe Abschnitt
3.2.1. (ORG). Je nach
Prozessorfamilie sind auch nicht alle Segmenttypen erlaubt.
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 ?
3.2.14. PHASE und DEPHASE
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:
Diese ,,Verschiebung'' wird mit dem Befehl
DEPHASE
wieder aufgehoben.
3.2.15. SAVE und RESTORE
Mit dem Gegenstück RESTORE wird entsprechend der
zuletzt gesicherte Zustand von diesem Stapel wieder heruntergeladen.
Diese beiden Befehle sind in erster Linie für Includefiles
definiert worden, um in diesen Dateien die obigen Variablen beliebig
verändern zu können, ohne ihren originalen Inhalt zu
verlieren. So kann es z.B. sinnvoll sein, in Includefiles mit
eigenen, ausgetesteten Unterprogrammen die Listingerzeugung
auszuschalten:
SAVE ; alten Zustand retten
LISTING OFF ; Papier sparen
.. ; der eigentliche Code
RESTORE ; wiederherstellen
Gegenüber einem einfachen LISTING OFF..ON-Pärchen
wird hier auch dann der korrekte Zustand wieder hergestellt, wenn die
Listingerzeugung bereits vorher ausgeschaltet war.
3.2.16. ASSUME
6809
68HC11K4
68HC12X
68HC16
H8/500
MELPS740
ASSUME SP:$1f ,
falls das interne ROM 8K groß ist.
MELPS7700/65816
Name
Bedeutung
Wertebereich
Default
DT/DBR
PG/PBR
DPR
X
MDatenbank
Code-Bank
direkt adr. Seite
Indexregisterbreite
Akkumulatorbreite0-$ff
0-$ff
0-$ffff
0 oder 1
0 oder 10
0
0
0
0MCS-196/296
8086
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.
CS:CODE, DS:DATA, ES:NOTHING, SS:NOTHING
XA
29K
80C166/167
ASSUME DPP0:0,DPP1:1,DPP2:2,DPP3:3
Der 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.
TLCS-47
ASSUME DMB:<0..3>
festgelegt werden. Der Default ist 0.
ST6
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.
78K2
78K3
assume rss:1
320C3x/C4x
ldp @adr
assume dp:adr>>16
.
.
.
ldi @adr,r2
75K0
F²MC16L
3.2.17. EMULATED
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!
3.2.18. Z80SYNTAX
3.3. Datendefinitionen
3.3.1. DC[.size]
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!
3.3.2. DS[.size]
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.
3.3.3. DB,DW,DD,DQ & DT
Gültigkeit: Intel (außer 4004/4040), Zilog, Toshiba, NEC, TMS370, Siemens, AMD, M16(C), MELPS7700/65816, National, ST9, TMS7000, TMS1000, µPD77230, Signetics, Fairchild, Intersil, XS1
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.
3.3.4. DS, DS8
Gültigkeit: Intel, Zilog, Toshiba, NEC, TMS370, Siemens, AMD, M16(C), National, ST9, TMS7000, TMS1000, Intersil
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 ;-)
3.3.5. BYT oder FCB
3.3.6. BYTE
3.3.7. DC8
3.3.8. ADR oder FDB
3.3.9. WORD
3.3.10. DW16
3.3.11. LONG
3.3.12. SINGLE, DOUBLE und EXTENDED
3.3.13. FLOAT und DOUBLE
3.3.14. EFLOAT, BFLOAT, TFLOAT
Gemeinsam ist den Befehlen, daß die Mantisse vor dem Exponenten
abgelegt wird (Lo-Word jeweils zuerst) und beide im Zweierkomplement
dargestellt werden. 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.15. Qxx und LQxx
q05 2.5 ; --> 0050h
lq20 ConstPI ; --> 43F7h 0032h
Mich möge niemand steinigen, wenn ich mich auf meinem HP28
verrechnet haben sollte...
3.3.16. DATA
3.3.17. ZERO
3.3.18. FB und FW
3.3.19. ASCII und ASCIZ
3.3.20. STRING und RSTRING
3.3.21. FCC
3.3.22. DFS oder RMB
3.3.23. BLOCK
3.3.24. SPACE
Gültigkeit: i960 Dito.
3.3.25. RES
3.3.26. BSS
3.3.27. DSB und DSW
3.3.28. DS16
3.3.29. ALIGN
align 2
macht den Programmzähler gerade. Wie auch bei DS.x 0
ist der freibleibende Speicherraum undefiniert.
3.3.30. LTORG
3.4. Makrobefehle
3.4.1. MACRO
<Name> MACRO [Parameterliste]
<Befehle>
ENDM
wird 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.
Diese eben beschriebenen Steuerparameter werden von AS aus der
Parameterliste ausgefiltert, haben also keine weitere Wirkung in der
folgenden Verarbeitung und Benutzung.
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
ENDM
Wichtig 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.
mul para=r0,parb=r1
Wiederum wird für nicht definierte Parameter ein eventuell
vorhandener Default oder ein Leerstring eingesetzt.
concat MACRO part1,part2
CALL part1_part2
ENDM
Der Aufruf
concat Modul,Funktion
ergibt also
CALL Modul_Funktion
WICHTIG: Die Namen dieser impliziten Parameter sind auch
case-insensitiv, wenn AS insgesamt angewiesen wurde, case-sensitiv zu
arbeiten!
instr1
|| instr2
(da die beiden Instruktionen im Maschinencode in ein Wort
verschmelzen, kann man die zweite Instruktion übrigens gar nicht
separat anspringen - man verliert also durch das Belegen der
Label-Position nichts). Das Problem ist aber, daß einige
'Bequemlichkeits-Befehle' durch Makros realisiert werden. Ein vor das
Makro geschriebenes Parallelisierungssymbol würde normalerweise
dem Makro selber zugeordnet, nicht dem ersten Befehl im Makro
selber. Aber mit diesem Trick funktioniert's:
myinstr macro {INTLABEL}
__LABEL__ instr2
endm
instr1
|| myinstr
Das Ergebnis nach der Expansion von myinstr ist identisch zu
dem vorherigen Beispiel ohne Makro.
push MACRO op
MOVE.ATTRIBUTE op,-(sp)
ENDM
pop MACRO op
MOVE.ATTRIBUTE (sp)+,op
ENDM
Schreibt man nun im Code
push d0
pop.l a2 ,
so wird daraus
MOVE. d0,-(sp)
MOVE.L (sp)+,a2
Eine Makrodefinition darf nicht über Includefilegrenzen
hinausgehen.
<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 ziel
Im 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.
srl macro op,n ; Schieben um n Stellen
rept n ; n einfache Befehle
!srl op
endm
endm
Fortan hat der SRL-Befehl einen weiteren Parameter...
3.4.2. IRP
IRP op, acc,b,dpl,dph
PUSH op
ENDM
was in folgendem resultiert:
PUSH acc
PUSH b
PUSH dpl
PUSH dph
Die Argumentliste darf analog zu einer Makro-Definition die
Steueranweisungen GLOBALSYMBOLS bzw.
NOGLOBALSYMBOLS (durch geschweifte Klammern als solche
gekennzeichnet) enthalten, um zu steuern, ob benutzte Labels für
jeden Durchgang automatisch lokal sind oder nicht.
3.4.3. IRPC
irpc char,"Hello World"
db 'CHAR'
endm
ACHTUNG! 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.
3.4.4. REPT
REPT 3
RR a
ENDM
rotiert den Akku um 3 Stellen nach rechts.
3.4.5. WHILE
cnt set 1
sq set cnt*cnt
while sq<=1000
dc.l sq
cnt set cnt+1
sq set cnt*cnt
endm
Dieses Beispiel legt alle Quadratzahlen bis 1000 im Speicher ab.
3.4.6. EXITM
3.4.7. SHIFT
pushlist macro reg
rept ARGCOUNT
push reg
shift
endm
endm
...weil das Makro einmal expandiert wird, seine Ausgabe von
REPT aufgenommen und dann n-fach ausgeführt wird. Das erste
Argument wird also n-fach gesichert...besser geht es schon so:
pushlist macro reg
if "REG"<>""
push reg
shift
pushlist ALLARGS
endif
endm
Also eine Rekursion, in der pro Schritt die Argumentliste (
ALLARGS) um eins verküzt wird. Der wichtige Trick ist,
daß jedes Mal eine neue Expansion gestartet wird...
3.4.8. MAXNEST
3.4.9. 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!
3.5. Strukturen
3.5.1. Definition
Des weiteren ist es möglich, die Verwendung des Punktes durch
den Befehl
dottedstructs <on|off>
dauerhaft ein- bzw. auszuschalten.
Rec STRUCT
Ident db ?
Pad db ?
Pointer dd ?
Rec ENDSTRUCT
Hier würde z.B. dem Symbol REC_LEN der Wert 6
zugewiesen.
3.5.2. Nutzung
thisrec Rec
reserviert Speicher in der Menge, wie er von der Struktur belegt
wird, und definiert gleichzeitig für jedes Element der Struktur
ein passendes Symbol mit dessen Adresse, in diesem Falle also
THISREC_IDENT, THISREC_PAD und THISREC_POINTER. Das
Label darf bei dem Aufruf einer Struktur naturgemäß nicht
fehlen; wenn doch, gibt's eine Fehlermeldung.
3.5.3. geschachtelte Strukturen
TreeRec struct
left dd ?
right dd ?
data Rec
TreeRec endstruct
TreeRec struct
left dd ?
right dd ?
TreeData struct
name db 32 dup(?)
id dw ?
TreeData endstruct
TreeRec endstruct
3.5.4. Unions
3.5.5. Namenlose Strukturen
TreeRec struct
left dd ?
right dd ?
struct
name db 32 dup(?)
id dw ?
endstruct
TreeRec endstruct
erzeugt also die Symbole TREEREC_NAME und
TREEREC_ID.
3.5.6. Strukturen und Sektionen
3.5.7. Strukturen und Makros
irp name,{GLOBALSYMBOLS},rec1,rec2,rec3
name Rec
endm
3.6. bedingte Assemblierung
3.6.1. IF / ELSEIF / ENDIF
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.6.2. SWITCH(SELECT) / 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.
3.7. Listing-Steuerung
3.7.1. PAGE(PAGESIZE)
3.7.2. NEWPAGE
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 3.7.3. MACEXP
MACEXP off
die Ausgabe des expandierten Codes komplett aus. Sinngemäß
schaltet ein
MACEXP on
die vollständige Listingform wieder an, dies ist auch die
Default-Vorgabe.
Die einzelnen Schalter wirken relativ zur aktuellen Einstellung: ist
z.B. initial alles eingeschaltet, sorgt ein
MACEXP noif,nomacro
dafür, daß nur noch das gelistet wird, was weder eine
Makrodefinition ist noch per bedingter Assemblierung ausgeschlossen
wurde.
3.7.4. LISTING
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.
3.7.5. PRTINIT und PRTEXIT
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.
3.7.6. TITLE
TITLE "\18\14Breiter Titel\15"
(Epson-Drucker schalten den Breitdruck automatisch am Zeilenende
aus.)
3.7.7. RADIX
3.7.8. OUTRADIX
3.8. 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.8.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.8.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.6 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.
3.8.3. PUBLIC und GLOBAL
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''.
3.8.4. FORWARD
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.8.5. Geschwindigkeitsaspekte
3.9. Diverses
3.9.1. SHARED
3.9.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!
3.9.3. BINCLUDE
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.9.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.
3.9.5. READ
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.
3.9.6. RELAXED
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.
3.9.7. END
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
4.1. 6811
,,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.
Über den ASSUME-Befehl teilt man AS mit, wie die
Banking-Register eingestellt sind und damit, wie und wo die
erweiterten Bereiche eingeblendet werden. Bei absoluten
Adressierungen mit Adressen jenseits $10000 berechnet AS dann
automatisch, welche Adresse innerhalb der ersten 64K anzusprechen
ist. Das kann natürlich wieder nur für direkte
Adressierungsarten funktionieren, bei indizierten/indirekten
Adreäusdrücken ist der Programmierer dafür
verantwortlich, über die momentan aktiven Banks den
Überblick zu behalten!
MMSIZ e1 MMWBR 84 MM1CR 00 MM2CR 80
Window 1: 10000...12000 --> 4000...6000
Window 1: 90000...94000 --> 8000...c000
ausgibt. Ein z.B. an Stelle $10000 liegender Befehl
jmp *+3
würde effektiv einen Sprung auf Adresse $4003 auslösen.
4.2. PowerPC
4.3. DSP56xxx
move x:var9 ,r0
move y:var10,r3 ,
der ist gekniffen, weil das Leerzeichen als Trennung paralleler
Datentransfers erkannt wird!
4.4. H8/300
4.5. SH7000/7600/7700
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).
4.6. HMCS400
Meta-Instruktion
Ersetzt
LD src, dest
XCH src, dest
ADD src, dest
ADC src, dest
SUB src, dest
SBC src, dest
OR src, dest
AND src, dest
EOR src, dest
CP cond, src, dest
BSET bit
BCLR bit
BTST bit LAI, LBI, LMID, LMIIY,
LAB, LBA, LAY, LASPX, LASPY, LAMR,
LWI, LXI, LYI, LXA, LYA, LAM, LAMD
LBM, LMA, LMAD, LMAIY, LMADY
XMRA, XSPX, XSPY, XMA, XMAD, XMB
AYY, AI, AM, AMD
AMC, AMCD
SYY
SMC, SMCD
OR, ORM, ORMD
ANM, ANMD
EORM, EORMD
INEM, INEMD, ANEM, ANEMD, BNEM,
YNEI, ILEM, ILEMD, ALEM, ALEMD,
BLEM, ALEI
SEC, SEM, SEMD
REC, REM, REMD
TC, TM, TMD
Operand
Typen
src, dest
cond
bit
bitpos A, B, X, Y, W, SPX, SPY (Register)
M (Speicher adressiert durch X/Y/W)
M+ (dito, mit Autoinkrement)
M- (dito, mit Autodekrement)
#val (2/4 bit immediate)
addr10 (Speicherzelle direkt)
MRn (Memory-Register 0..15)
NE (ungleich)
LE (kleiner oder gleich)
CA (Carry)
bitpos,M
bitpos,addr10
0..34.7. OLMS-40
Meta-Instruktion
Ersetzt
LD dest, src
DEC dest
INC dest
BSET bit
BCLR bit
BTST bit LAI, LLI, LHI, L,
LAL, LLA, LAW, LAX, LAY, LAZ,
LWA, LXA, LYA, LPA, LTI, RTH, RTL
DCA, DCL, DCM, DCW, DCX, DCY, DCZ, DCH
INA, INL, INM, INW, INX, INY, INZ
SPB, SMB, SC
RPB, RMB, RC
TAB, TMB, Tc
Operand
Typen
src, dest
bit
bitpos A, W, X, Y, Z, DPL, DPH (Register)
T, TL, TH (Timer, obere/untere Hälfte)
(DP), M (Speicher adressiert durch DPH/DPL)
#val (4/8 bit immediate)
PP (Port-Pointer)
C (Carry)
(PP), bitpos
(DP), bitpos
(A), bitpos
0..34.8. OLMS-50
assume p:<Wert>
mit, z.B. direkt nach einem PAGE-Befehl.
4.9. MELPS-4500
bl $1234
anstelle
bl $24,$34 .
4.10. 6502UNDOC
& 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 4.11. MELPS-740
4.12. 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.
4.13. M16
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!
4.14. 4004/4040
4.15. MCS-48
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.
4.16. MCS-51
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.
4.17. MCS-251
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.
4.18. 8080/8085
Z80SYNTAX ON
für die allermeisten 8080/8085-Befehle möglich, sie auch
wahlweise im 'Z80-Stil' zu schreiben, d.h. mit weniger Mnemonics,
dafür aber mit deutlich aussagekräftigeren Operanden.
Für die folgenden Befehle ist die Z80-Syntax nicht möglich,
da sie mit existierenden 8080-Mnemonics kollidieren:
4.19. 8085UNDOC
Anweisung : DSUB [reg] Funktion : HL <- HL - reg Flags : CY, S, X5, AC, Z, V, P Argumente : reg = B für BC (optional)
Anweisung : ARHL Funktion : HL,CY <- HL >> 1 (arithmetisch) Flags : CY Argumente : keine
Anweisung : RDEL Funktion : CY,DE <- DE << 1 Flags : CY, V Argumente : keine
Anweisung : LDHI d8 Funktion : DE <- HL + d8 Flags : keine Argumente : d8 = 8-Bit-Konstante
Anweisung : LDSI d8 Funktion : DE <- SP + d8 Flags : keine Argumente : d8 = 8-Bit-Konstante
Anweisung : RST flag Funktion : Restart zu 40h wenn flag=1 Flags : keine Argumente : flag = V für Overflow-Bit
Anweisung : SHLX [reg] Funktion : [reg] <- HL Flags : keine Argumente : reg = D für DE (optional)
Anweisung : LHLX [reg] Funktion : HL <-[reg] Flags : keine Argumente : reg = D für DE (optional)
Anweisung : JNX5 adr Funktion : springe zu adr wenn X5=0 Flags : keine Argumente : adr = absolute 16-Bit-Adresse
Anweisung : JX5 adr Funktion : springe zu adr wenn X5=1 Flags : keine Argumente : adr = absolute 16-Bit-Adresse 4.20. 8086..V35
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.5 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]
FINIT
FSTSW [vari]
wird so z.B.
FNINIT
FNSTSW [vari]
Diese Variante ist bei allen Koprozessorbefehlen erlaubt.
4.21. 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...
4.22. XA
4.23. AVR
4.24. Z80UNDOC
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...
4.25. Z380
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.26. Z8 und eZ8
op1 equ 040h
op2 equ 041h
srp #040h
assume rp:040h
ld op1,op2 ; entspricht ld r0,r1
Im Gegensatz zum Original Zilog-Assembler ist es nicht erforderlich,
eine 'Arbeitsregisteradressierung' explizit durch ein vorangestelltes
Ausrufezeichen anzufordern, wobei AS diese Syntax nichtsdestotrotz
versteht - ein vorangestelltes Ausrufezeichen erzwingt quasi
4-Bit-Adressierung, auch wenn die Adresse eigentlich nicht im durch
RP festgelegten 16-Bit-Fenster liegt (dann wird eine Warnung
ausgegeben). Umgekehrt ist es durch ein vorangestelltes >-Zeichen
möglich, eine Adressierung mit 8 Bit zu erzwingen, auch wenn die
Adresse eigentlich im aktuellen 16er-Fenster liegt.
4.27. 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.6).
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
4.28. TLCS-90
4.29. TLCS-870
LD CF,A.7 ; Akku Bit 7 nach Carry
LD C,A.7 ; Konstante A.7 nach Register C
4.30. TLCS-47
4.31. TLCS-9000
und dieses Target foratn in einen Dornröschenschlaf fiel...
ld:g.b (0h),0 ; kein Pr"afix
ld:g.b (400000h),0 ; Pr"afix automatisch erzeugt
ld:g.b (>0h),0 ; Pr"afix erzwungen
4.32. TC9331
4.33. 29xxx
4.34. 80C16x
jmps 12345h
anstelle von
jmps 1,2345h
Leider 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.
bclr r5.15+1
Hier muß ein BIT her:
msb bit r5.15
.
.
.
bclr msb+1
Fü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 #n
Dieser 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.
Damit das etwas klarer wird, ein Beispiel (die DPP-Register haben die
Reset-Vorbelegung) :
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
4.35. PIC16C5x/16C8x
Bei den Befehlen, die das Register W mit einem anderen Register
verknüpfen, muß normalerweise als zweiter Parameter
angegeben werden, ob das Ergebnis in W oder im Register abgelegt
werden soll. Bei diesem Assembler ist es erlaubt, den zweiten
Parameter wegzulassen. Welches Ziel dann angenommen werden soll,
hängt vom Typ des Befehls ab: bei unären Operationen wird
defaultmäßig das Ergebnis zurück ins Register gelegt.
Diese Befehle sind:
COMF, DECF, DECFSZ, INCF, INCFSZ, RLF, RRF und
SWAPF
Die anderen Befehle betrachten W defaultmäßig als
Akkumulator, zu dem ein Register verknüpft wird:
ADDWF, ANDWF, IORWF, MOVF, SUBWF und XORWF
4.36. PIC17C4x
MOVLW <Adr15..8>
MOWF 3
LCALL <Adr0..7>
4.37. ST6
Befehl
in Wirklichkeit
CLR A
SLA A
CLR adr
NOP SUB A,A
ADD A,A
LDI adr,0
JRZ PC+1
ASCII, ASCIZ, BLOCK, BYTE, END, ENDM, EQU, ERROR, MACRO,
ORG, TITLE, WARNING
Tabelle 4.8 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
4.38. ST7
4.39. ST9
rn.[!]b
wobei ! 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,!bit2
Auf diese Weise ist es auch möglich, eine inverse Definition
nachträglich wieder aufzuheben.
4.40. 6804
4.41. TMS3201x
4.42. TMS320C2x
BSS, STRING, RSTRING, BYTE, WORD , LONG, FLOAT
Sollten 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.
4.43. TMS320C3x/C4x
4.44. TMS9900
Weiterhin wechselt TI mit der Registerbezeichnung zwischen
Rx und WRx...vorerst ist beides zugelassen.
4.45. TMS70Cxx
Wichtig - diese Varianten sind nur beim TMS70Cxx zugelassen -
entsprechende 7000er-Varianten sind bei den 370ern nicht erlaubt!
4.46. TMS370xxx
defbit macro name,bit,adr
name equ adr+(bit<<16)
endm
aber 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...
4.47. MSP430(X)
rlc @r6+
automatisch in
addc @r6+,-2(r6)
umgesetzt.
4.48. TMS1000
4.49. COP8 & SC/MP
4.50. SC144xxx
IAR
AS
Funktion
#include
#define
#elif, ELIF, ELSEIF
#else, ELSE
#endif, ENDIF
#error
#if, IF
#ifdef
#ifndef
#message
=, DEFINE, EQU
EVEN
COL, PAGSIZ
ENDR
LSTCND, LSTOUT
LSTEXP, LSTREP
LSTXRF
PAGE
REPTC include
SET, EQU
ELSEIF
ELSE
ENDIF
ERROR, FATAL
IF
IFDEF
IFNDEF
MESSAGE
=, EQU
ALIGN 2
PAGE
ENDM
LISTING
MACEXP
<Kommandozeile>
NEWPAGE
IRPCInclude-Datei einbinden
Symbole definieren
Weiterer Zweig einer IF-Kette
Letzter Zweig einer IF-Kette
Beendet eine IF-Kette
Fehlermeldung erzeugen
Beginn einer IF-Kette
Symbol definiert ?
Symbol nicht definiert ?
Nachricht ausgeben
Feste Wertzuweisung
Programmzähler gerade machen
Seitengröße für Listing setzen
Ende einer REPT-Struktur
Umfang des Listings steuern
Expandierte Makros anzeigen?
Querverweisliste erzeugen
Neue Seite im Listing
Repetition mit Zeichenersetzung4.51. 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äß [80] 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.52. 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.
4.53. 78K2/78K3
4.54. uPD772x
4.55. F2MC16L
5. Dateiformate
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
$03
$05
$07
$11
$13
$15
$19
$25
$29
$31
$33
$38
$3a
$3c
$3f
$42
$48
$4a
$4c
$51
$53
$55
$57
$59
$5b
$5d
$5f
$61
$63
$65
$67
$69
$6b680x0, 6833x
M*Core
PowerPC
TMS1000
65xx/MELPS-740
M16
F2MC8L
65816/MELPS-7700
SYM53C8xx
29xxx
MCS-51
ST7
1802/1805
8X30x
XA
4004/4040
8086..V35
TMS9900
MSP430
80C166/167
Z80/180/380
TLCS-90
TLCS-47
TLCS-870/C
eZ8
KCPSM3
NEC 75xx
COP4
6800, 6301, 6811
6809
68HC16
ACE
H8/500
KCPSM$02
$04
$06
$09
$12
$14
$16
$21
$27
$2a
$32
$37
$39
$3b
$3e
$41
$47
$49
$4b
$4f
$52
$54
$56
$58
$5a
$5c
$5e
$60
$62
$64
$66
$68
$6a
$6cATARI_VECTOR
XGATE
XCore
DSP56xxx
MELPS-4500
M16C
F2MC16L
MCS-48
KENBAK
i960
ST9
2650
MCS-96/196/296
AVR
8008
8080/8085
TMS320C6x
TMS370xxx
TMS320C54x
MIL STD 1750
TLCS-900
TLCS-870
TLCS-9000
NEC 78K3
TC9331
LatticeMico8
68RS08
78K2
6805/HC08
6804
68HC12
H8/300(H)
807x
SH7000
Header
Familie
Header
Familie
$6d
$6f
$71
$73
$75
$77
$79
$7b
$7d
$7fSC14xxx
COP8
PIC16C5x
TMS-7000
TMS320C2x
TMS320C20x/C5x
Z8
75K0
µPD7720
µPD77230$6e
$70
$72
$74
$76
$78
$7a
$7c
$7e
SC/MP
PIC16C8x
PIC17C4x
TMS3201x
TMS320C3x/C4x
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
Dateiformatfehler6.1. PLIST
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.
6.2. BIND
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.
6.3. P2HEX
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. Diese
Automatik läßt sich mit der Kommandozeilenoption
-M <1|2|3>
teilweise unterdrücken: Ein Wert von 2 bzw. 3 sorgt dafür,
daß S-Records mit einem Mindesttyp von 2 bzw. 3 benutzt werden,
während ein Wert von 0 der vollen Automatik entspricht.
-avrlen <2|3>
die Länge zur Not auf 2 Bytes reduzieren.
-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
[8] 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. Um im Gegenteil die Adreßlage auf einen bestimmten
Wert zu verschieben, kann man den Schalter
-R <Wert>
verwenden. Der dabei angegebene Wert ist ein Offset, d.h. er wird auf
die in der Code-Datei angegebenen Adressen aufaddiert.
-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. Diese Option
sollte nicht mehr in neuen Projekten verwendet werden, da P2HEX
inzwischen direkt Daten aus dem Datensegment korrekt umsetzen kann.
-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.
6.4. P2BIN
-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.
6.5. AS2MSG
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
Warnung
Ein einzelner X-Operand kann sowohl als Register X als auch
X-inidizierte Adressierung mit Null-Displacement
interpretiert werden, da sich Morola hier nicht festlegt. AS
wählt die letztere Variante, was möglicherweise
nicht das erwartete ist.
keines
Warnung
Die Instruktion arbeitet nur auf Byte- bzw.
Langwort-Operanden, Bitnummern jenseits 7 bzw. 31 werden von
der CPU modulo-8 bzw. modulo-32 behandelt werden.
keines
Warnung
Gültige bzw. sinnvolle Werte für den Registerzeiger
sind nur Werte von 0x00...0x70 bzw. 0xf0, weil die anderen
Registerbereiche unbelegt sind.
keines
Warnung
Einem Makroparameter wurden zwei oder mehr verschiedene Werte
zugewiesen. Dies kann bei der Verwendung von
Schlüsselwortparametern auftreten. Das zuletzt
angegebene Argument wird benutzt.
Name des Makroparameters
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
Das an einem Befehl angefügte Attribut ist
ungültig.
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
Die benutzte Kombination von Bedingungen kann nicht in einem
Befehl verwendet werden.
die Bedingung, bei der die Unverträglichkeit entdeckt
wurde.
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, plus die Position der
Vorwärts-Deklaration im Quelltext
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
Diese Maschinenanweisung kann nicht mit Hilfe eines
RPT-Konstruktes wiederholt werden.
keines
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 Schlüsselwortargument bezog sich auf einen
Parameter, den das aufgerufene Makro gar nicht besitzt.
verwendetes Schlüsselwort bzw. Makroparameter
Fehler
Positions- und Schlüsselwortargumente dürfen in
einem Makroaufruf gemischt werden, aber nach dem ersten
Schlüsselwortargument sind nur noch solche
zugelassen.
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
Ein Anweisungspaket dard nicht über eine
32-Byte-Adreßgrenze reichen.
keines
Fehler
Eine der Ausführungseinheiten des Prozessors wurde in
einem Anweisungspaket mehrfach benutzt.
der Name der Funktionseinheit
Fehler
Ein Ausführungspaket enthält mehr als eine
Lang-Leseoperation, was nicht erlaubt ist.
eine der Funktionseinheiten, auf denen eine
Lang-Leseoperation ausgeführt wird
Fehler
Ein Ausführungspaket enthält mehr als eine
Lang-Schreiboperation, was nicht erlaubt ist.
eine der Funktionseinheiten, auf denen eine
Lang-Schreiboperation ausgeführt wird
Fehler
Ein Ausführungspaket enthält sowohl eine
Lang-Leseoperation als auch eine Schreiboperation, was nicht
erlaubt ist.
eine der Funktionseinheiten, deren Operationen im Konflikt
stehen.
Fehler
Auf das gleiche Register wurde mehr als viermal im gleichen
Anweisungspaket Bezug genommen.
der Name des Registers, das zu oft referenziert wurde
Fehler
Auf das gleiche Register wurde mehrfach im gleichen
Ausführungspaket geschrieben, was nicht erlaubt
ist.
der Name der fraglichen Registers
Fehler
Ein Anweisungspaket beinhaltet mehr als einen direkten
Sprung, was nicht erlaubt ist.
keines
Fehler
Diese Anweisung kann nicht auf dieser Funktionseinheit
ausgeführt werden.
none
Fehler
Das mit einem Backslash eingeleitete Sonderzeichen ist nicht
definiert.
keines
Fehler
Die angegebene Kombination von Präfixen ist nicht
zulässig oder nicht im Maschinenkode darstellbar.
keines
Fehler
Ein einmal mit EQU als Konstante
definiertes Symbol kann nicht nachträglich mit
SET verändert werden.
der Name des fraglichen Symbols
Fehler
Ein einmal mit SET als Variable
definiertes Symbol kann nicht nachträglich als
Konstante deklariert werden (z.B. mit EQU.
der Name des fraglichen Symbols
Fehler
Bei einer Strukturdefinition fehlt der zugehörende Name
für die Struktur.
keines
Fehler
In der Argumentenliste dieser Anweisung dürfen keine
Leerstrings benutzt werden.
keines
Fehler
Der benutzte Maschinenbefehl ist dem Assembler zwar bekannt,
ist aber aufgrund fehlender Dokumentation seitens des
Prozessorherstellers momentan nicht implementiert.
Der benutzte Befehl
Fehler
Eine Struktur oder Union, die keinen Namen hat, muß
immer Teil einer anderen, benamten Struktur oder Union
sein.
keines
Fehler
ENDUNION darf nur zum Beenden der Definition einer Union
benutzt werden, nicht einer Struktur.
Name der Struktur (falls vorhanden)
Fehler
Die Zieladresse befindet sich nicht in der durch das
Seitenregister aktuell adressierbaren Speicherseite.
keines
Fehler
Ein MACEXP gegebenes Argument konnte nicht
interpretiert werden.
das unbekannte Argument
Fehler
Eine Angabe zur Makroexpansion und ihr genaues Gegenteil
dürfen nicht gleichzeitig al Argument von
MACEXP verwendet werden.
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
B. E/A-Fehlermeldungen
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.C. Häufig gestellte Fragen
awk '{print $0"\r"}' test.hex >test_cr.hex
D. Pseudobefehle gesammelt
Immer vorhandene Befehle
Zusätzlich existiert SET bzw. EVAL,
falls SET bereits ein Prozessorbefehl ist.
= := ALIGN BINCLUDE CASE CHARSET CPU DEPHASE DOTTEDSTRUCTS ELSE ELSECASE ELSEIF END ENDCASE ENDIF ENDM ENDS 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 RORG SAVE SECTION SEGMENT SHARED STRUC STRUCT SWITCH TITLE UNION 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 REG SUPMODE Motorola M-Core
DC[.<size>] DS[.<size>] REG SUPMODE Motorola XGATE
ADR BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING RMB Motorola 68xx/Hitachi 63xx
ADR BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING RMB Motorola/Freescale 6805/68HC(S)08
ADR BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING RMB Motorola 6809/Hitachi 6309
ADR ASSUME BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING RMB Motorola 68HC12
ADR BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING RMB Motorola 68HC16
ADR ASSUME BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING RMB Freescale 68RS08
ADR ASSUME BYT DC[.<size>] DFS DS[.<size>] FCB FCC FDB PADDING 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 Hitachi HMCS400
DATA RES SFR 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 4004
DATA DS REG Intel 8008
DB DD DQ DS DT DW Intel MCS-48
DB DD DQ DS DT DW REG Intel MCS-(2)51
BIGENDIAN BIT DB DD DQ DS DT DW PORT REG 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 Signetics 2650
DB DD DQ DS DT DW Philips XA
ASSUME BIT DB DC[.<size>] DD DQ DS[.<size>] DT DW PADDING PORT SUPMODE Atmel AVR
DATA PACKING PORT REG RES AMD 29K
ASSUME DB DD DQ DS DT DW EMULATED SUPMODE Siemens 80C166/167
ASSUME BIT DB DD DQ DS DT DW REG Zilog Zx80
DB DD DEFB DEFW DQ DS DT DW EXTMODE LWORDMODE Zilog Z8
DB DD DQ DS DT DW REG SFR Xilinx KCPSM
CONSTANT NAMEREG REG Xilinx KCPSM3
CONSTANT DB DD DQ DS DT DW NAMEREG PORT REG LatticeMico8
DB DD DQ DS DT DW PORT REG 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 PIC16C8x
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/C4x
ASSUME BSS DATA EXTENDED SINGLE WORD Texas TM32C020x/TM32C05x/TM32C054x
BFLOAT BSS BYTE DATA DOUBLE EFLOAT TFLOAT LONG LQxx PORT Qxx RES RSTRING STRING WORD Texas TMS320C6x
BSS DATA DOUBLE SINGLE 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 INS807x
DB DD DQ DS DT DW National COP4
ADDR ADDRW BYTE DB DD DQ DS DSB DSW DT FB FW SFR WORD National COP8
ADDR ADDRW BYTE DB DD DQ DS DSB DSW DT FB FW SFR WORD National COP8
DC DC8 DS DS8 DS16 DW DW16 Fairchild ACE
DB DD DQ DS DT DW NEC µPD78(C)1x
ASSUME DB DD DQ DS DT DW NEC 75xx
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 78K2
BIT DB DD DQ DS DT DW NEC 78K3
BIT DB DD DQ DS DT DW NEC µPD772x
DATA RES NEC µPD772x
DS DW Symbios Logic SYM53C8xx
Fujitsu F2MC8L
DB DD DQ DS DT DW Fujitsu F2MC16L
DB DD DQ DS DT DW OKI OLMS-40
DATA RES SFR OKI OLMS-50
DATA RES SFR Mitsubishi M16C
DB DD DQ DS DT DW XMOS XS1
DB DD DQ DS DT DW REG MIL STD 1750
DATA EXTENDED FLOAT 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 ?
MACEXP
Boolean
dynam.(1)
Expansion von Makrokon-
strukten im Listing
freigeschaltet ?
Name
Datentyp
Definition
Bedeutung
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, fall außer-
halb aller Sektionen
MOMSEGMENT
String
Spezial
Name des mit SEGMENT ein-
gestellten Adreßraumes
NESTMAX
Integer
dynam.(256)
maximale Verschachtelungs-
tiefe für Makros
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
WRAPMODE
Integer
vordef.
verkürzter Programmzähler
angenommen?
*
Integer
Spezial
mom. Programmzähler (Motorola,
Rockwell, Microchip, Hitachi)
$
Integer
Spezial
mom. Programmzähler (Intel,
Zilog, Texas, Toshiba, NEC,
Siemens, AMD)F. Mitgelieferte Includes
F.1. BITFUNCS.INC
F.2. CTYPE.INC
G. Danksagungen
''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
I.1. Verwendete Sprache
I.2. Abfangen von Systemabhängigkeiten
I.3. Systemunabhängige Dateien
I.3.1. Von AS genutzte Module
as.c
asmallg.c
asmcode.c
asmdebug.c
asmdef.c
asmfnums.c
asmif.c
asminclist.c
asmitree.c
asmmac.c
asmpars.c
asmsub.c
bpemu.c
chunks.c
cmdarg.c
Dieses Modul wird nicht nur von AS, sondern auch von den
Hilfsprogrammen BIND, P2HEX und P2BIN verwendet.
codepseudo.c
codevars.c
endian.c
headids.c
ioerrs.c
nlmessages.c
nls.c
stdhandl.c
stringlists.c
strutil.c
version.c
code????.c
I.3.2. Zusätzliche Module für die Hilfsprogramme
hex.c
p2bin.c
p2hex.c
pbind.c
plist.c
toolutils.c
I.4. Während der Erzeugung von AS gebrauchte Module
a2k.c
addcr.c
bincmp.c
findhyphen.c
grhyph.c
rescomp.c
tex2doc.c
tex2html.c
umlaut.c und unumlaut.c
ushyph.c
I.5. Generierung der Nachrichtendateien
I.5.1. Format der Quelldateien
Include <Datei>
Langs DE(049) EN(001,061)
beschreibt, daß zwei Sprachen im folgenden definiert werden.
Der erste Nachrichtensatz soll benutzt werden, wenn unter Unix die
Sprache per Environment-Variablen auf DE gestellt wurde bzw.
unter DOS bzw. OS/2 der Landescode 049 eingestellt wurde. Der zweite
Satz kommt dementsprechend bei den Einstellungen EN bzw. 061
oder 001 zum Einsatz. Während bei den 'Telefonnummern' mehrere
Codes auf einen Nachrichtensatz verweisen können, ist die
Zuordnung zu den Unix-Landescodes eineindeutig. Dies ist in der
Praxis aber kein Beinbruch, weil die LANG-Variablen unter
Unix Unterversionen einer Sprache als Anhängsel beschreiben,
z.B. so:
de.de
de.ch
en.us
AS vergleicht nur den Anfang der Strings und kommt so trotzdem zur
richtigen Entscheidung. Das Default-Statement gibt vor,
welcher Sprachensatz verwendet werden soll, wenn entweder
überhaupt keine Sprache gesetzt wurde oder eine Kennung
verwendet wird, die nicht in der Liste von Langs vorhanden
ist. Typischerweise ist dies Englisch:
Default EN
Nach diesen beiden Definitionen folgt eine beliebige Menge von
Message-Statements, d.h. Definitionen von Meldungen:
Message ErrName
": Fehler "
": error "
Wurden n Sprachen im Langs-Statement angekündigt, so
nimmt der Message-Compiler genau die folgenden n Zeilen als
die zu speichernden Strings. Es ist also nicht möglich, bei
einzelnen Nachrichten bestimmte Sprachen fortzulassen, und eine auf
die Strings folgende Leerzeile ist keinesfalls als Endemarkierung
für die Liste mißzuverstehen; eingefügte Leerzeilen
dienen einzig und allein der besseren Lesbarkeit. Was allerdings
erlaubt ist, ist, einzelne Meldungen über mehrere Zeilen in der
Quelldatei zu verteilen; alle Zeilen bis auf die letzte müssen
dann mit einem Backslash als Fortsetzungszeichen enden:
Message TestMessage2
"Dies ist eine" \
"zweizeilige Nachricht"
"This is a" \
"two-line message"
Wie bereits erwähnt, handelt es sich bei den Quelldateien um
reine ASCII-Dateien; Sonderzeichen können in den Meldungstexten
zwar eingetragen werden (und der Compiler wird sie auch so
durchreichen), der gravierende Nachteil ist aber, daß eine
solche Datei nicht mehr voll portabel ist: Wird sie auf ein anderes
System gebracht, das z.B. eine andere Kodierung für Umlaute
verwendet, bekommt der Anwender zur Laufzeit nur merkwürdige
Zeichen zu sehen...Sonderzeichern sollten daher immer mit Hilfe von
speziellen Sequenzen geschrieben werden, die von HTML bzw. SGML
entlehnt wurden (siehe Tabelle I.1).
Zeilenvorschübe können in eine Zeile wie von C her gewohnt
mit \n eingebracht werden.
Sequenz...
ergibt...
ä ö ü
Ä Ö Ü
ß
à è ì ò ù
À È Ì Ò Ù
á é í ó ú
Á É Í Ó Ú
â ê î ô û
Â Ê Î Ô Û
ç Ç
ñ Ñ
å Å
æ &Aelig;
¿ ¡ä ö ü (Umlaute)
Ä Ö Ü
ß (scharfes s)
á é í ó ú (Accent
Á É Í Ó Ú grave)
à è ì ò ù (Accent
À È Ì Ò Ù agiu)
â ê î ô û (Accent
Â Ê Î Ô Û circonflex)
ç Ç(Cedilla)
ñ Ñ
å Å
æ Æ
umgedrehtes ! oder ?I.6. Dokumentationserzeugung
make docs
angestoßen; daraufhin werden die beiden erwähnten
Hilfstools erzeugt, auf die TeX-Dokumentation angewandt und
schlußendlich wird noch LaTeX selber aufgerufen. Dies
natürlich für alle Sprachen nacheinander...
I.7. Testsuite
I.8. Einhängen eines neuen Zielprozessors
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!!
AddCopyright("Intel 80986-Codegenerator (C) 2010 Hubert Simpel");
Der übergebene String wird dann nach dem Programmstart
zusätzlich zu der Standardmeldung ausgegeben.
Schreiben des Codegenerators selber
Mit Sicherheit wird auch das Studium der vorhandenen Module
weiterhelfen.
Änderungen für die Dienstprogramme
I.9. Lokalisierung auf eine neue Sprache
Literaturverzeichnis
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).
(part of the AVR tools documentation)
G65SC802/G65SC816 CMOS 8/16-Bit Microprocessor.
Family Data Sheet.
COP410L/COP411L/COP310L/COP311L Single-Chip N-Channel
Microcontrollers. RRD-B30M105, March 1992
COPS Family User's Guide.
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
ACE1101 Data Sheet.
Preliminary, May 1999
ACE1202 Data Sheet.
Preliminary, May 1999
ACEx Guide to Developer Tools. AN-8004, Version 1.3 September
1998
S12XCPUV1 Reference Manual. S12XCPUV1, v01.01, 03/2005
RS08 Core Reference Manual. RS08RM, Rev. 1.0, 04/2006
MC9S12XDP512 Data Sheet. MC9S12XDP512, Rev. 2.11, 5/2005
June 1998 Semiconductor Data Book.
CD00-00981-1E
F²MC16LX 16-Bit Microcontroller MB90500 Series Programming
Manual.
CM44-00201-1E, 1998
8-/16-Bit Microprocessor Data Book.
1986
Understanding HD6301X/03X CMOS Microprocessor Systems.
published by Hitachi
H8/300H Series Programming Manual.
(21-032, no year of release given)
http://archaicpixels.com/HuC6280_Instruction_Set
R65C19 Microcomputer Data Sheet.
Document Number 29400N10, January 1992
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, no year of release given)
H8/534,H8/536 Hardware Manual.
(21-19A, no year of release given)
PPC403GA Embedded Controller User's Manual.
First Edition, September 1994
Embedded Controller Handbook.
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
4040: Single-Chip 4-Bit P-Channel Microprocessor.
(no year of release given)
CDP1802A, CDP1802AC, CDP1802BC CMOS 8-Bit Microprocessors.
March 1997
CDP1805AC, CDP1806AC CMOS 8-Bit Microprocessor with On-Chip RAM
and Counter/Timer.
March 1997
A memo on the secret features of 6309.
(available via World Wide Web:
http://www.cs.umd.edu/users/fms/comp/CPUs/6309.txt)
LatticeMico8 Microcontroller Users Guide.
Reference Design RD1026, February 2008
Microchip Data Book.
1993 Edition
Military Standard Sixteen-Bit Computer Instruction Set
Architecture.
MIL-STD-1750A (USAF), 2 July 1980
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.
Second Edition, Prentice-Hall, Englewood Cliffs 1989
MC68851 Paged Memory Management Unit User's Manual.
Second Edition, Prentice-Hall, Englewood Cliffs 1989,1988
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
PowerPC(tm) MPC821 Portable Microprocessor User's Manual.
1996
CPU12 Reference Manual.
1st edition, 1996
CPU08 Reference Manual.
Rev. 1 (no year of release given 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 (no year of release given im PDF-File)
MC68HC11K4 Technical Data. 1992
Microcontroller Data Book. Second Edition, December 1986
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
SC14xxx DIP commands Reference guide.
Application Note AN-D-031, Version 0.4, 12-28-1998
INS8070-Series Microprocessor Family. October 1980
µpD70108/µpD70116/µpD70208/µpD70216/µ
pD72091 Data Book.
(no year of release given)
User's Manual µCOM-87 AD Family.
(no year of release given)
µCOM-75x Family 4-bit CMOS Microcomputer User's
Manual.
Vol. I+II (no year of release given)
78K/II Series 8-Bit Single-Chip Microcontroller User's Manual -
Instructions.
Document No. U10228EJ6V0UM00 (6th edition), December 1995
uPD78310/312CW/G 8 Bit CMOS Microcomputer Product Description.
Digital Signal Processor Product Description.
PDDSP.....067V20 (no year of release given)
µ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
TMS320C4x User's Guide.
SPRU063C, May 1999
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
MSP430 MSP430x5xx and MSP430x6xx Family User's Guide.
October 2016, SLAU208
TMS320C62xx CPU and Instruction Set Reference Manual.
Jan. 1997, SPRU189A
TMS320C20x User's Guide.
April 1999, SPRU127C
TMS320C54x DSP Reference Set; Volume 1: CPU and Peripherals.
March 2001, SPRU172C
TMS320C54x DSP; Volume 2: Mnemonic Instruction Set.
March 2001, SPRU172C
TMS 1000 Series MOS/LSI One-Chip Microcomputers Programmer's
Reference Manual.
CM122-1 1275, 1975
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
TC9331 Digital Audio Signal Processor Application
Information.
Bipolare Mikroprozessoren und bipolare LSI-Schaltungen.
Datenbuch, 1985, ISBN 3-87095-186-9
PicoBlaze 8-Bit Microcontroller for Virtex-E and Spartan-II/IIE
Devices.
Application Note XAPP213, Version 2.1, February 2003
PicoBlaze 8-bit Embedded Microcontroller User Guide for
Spartan-3, Virtex-II, and Virtex-II Pro FPGAs.
UG129 (v1.1) June 10, 2004
The XMOS XS1 Architecture.
Publication Date: 2009/10/19, Copyright 2009 XMOS Ltd.
Z8 Microcontrollers Databook.
1992
Discrete Z8 Microcontrollers Databook.
(no year of release given)
Z380 CPU Central Processing Unit User's Manual.
(no year of release given)
eZ8 CPU User Manual.
UM01285-0503
''Ich schlage vor, dem Parlament ein Gesetz vorzulegen,
das einem Autor, der ein Buch ohne Index publiziert,
das Copyright entzieht und ihn außerdem für sein
Vergehen
mit einer Geldstrafe belegt.''
--Lord John Campbell
Index
ADDR 1
ADDRW 1
ADR 1
ALIGN 1
ASCII 1
ASCIZ 1
ASSUME 1
BFLOAT 1
BIGENDIAN 1
BINCLUDE 1
BIT 1
BLOCK 1
BSS 1
BYT 1
BYTE 1 2
CASE 1
CHARSET 1
CODEPAGE 1
CONSTANT 1
CPU 1
DATA 1
DB 1
DBIT 1
DC 1
DC8 1
DD 1
DEFB 1
DEFW 1
DEPHASE 1
DFS 1
DOTTEDSTRUCTS 1
DOUBLE 1 2
DQ 1
DS 1 2
DS16 1
DS8 1
DSB 1
DSW 1
DT 1
DUP 1
DW 1
DW16 1
EFLOAT 1
ELSE 1
ELSECASE 1
ELSEIF 1
EMULATED 1
END 1
ENDCASE 1
ENDIF 1
ENDM 1
ENDS 1
ENDSTRUC 1
ENDSTRUCT 1
ENDUNION 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
MAXNEST 1
MESSAGE 1
NAMEREG 1
NEWPAGE 1
ORG 1
OUTRADIX 1
PACKING 1
PADDING 1
PAGE 1
PAGESIZE 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
RORG 1
RSTRING 1
Registersymbole 1
SAVE 1
SEGMENT 1
SELECT 1
SET 1
SFR 1
SFRB 1
SHARED 1 2 3
SHIFT 1
SINGLE 1
SPACE 1
SRCMODE 1
STRING 1
STRUC 1
STRUCT 1
SUPMODE 1
SWITCH 1
TFLOAT 1
TITLE 1
UNION 1
WARNING 1
WHILE 1
WORD 1 2
WRAPMODE 1
XSFR 1
YSFR 1
Z80SYNTAX 1
ZERO 1