Dieser Text dient zur Dokumentation von INLASS. INLASS ist ein Assem- blerprogramm, das direkt INLINE-Code für Turbo-Pascal erzeugt, woher auch sein Name stammt (INLine ASSembler). Die Urfassung stammt aus dem CHIP-Sonderheft Turbo-Pascal 2, ist von mir jedoch erweitert worden. Der erste Teil des Textes entstammt original dem CHIP-Sonderheft, im zweiten Teil beschreibe ich die Änderungen, die ich vorgenommen habe, im dritten Teil wird ein Beispiel gezeigt. 1. Teil: INLASS - Features: Bedingte Assemblierung mit ELSE-Zweig und beliebiger Verschachtelung (maximale Tiefe=255). MAKROS mit lokalen Variablen und Fähigkeit zu rekursiven Aufrufen. Frei verschiebbarer INLINE-Code. Da alle Adressen immer relativ be- stimmt werden, können die generierten INLINE-Anweisungen an jeder beliebigen Stelle eines Programms eingesetzt werden. Der Assembler-Quelltext Aus Platzgründen muß die Beschreibung von INLASS ein wenig kompakt ausfallen. Der Quelltext (=SOURCE) wird zeilenweise übersetzt. Damit eine Zeile verarbeitet werden kann, muß sie folgenden Aufbau haben: >> <;KOMMENTAR> <> bedeutet, daß der eingeklammerte Teil auch entfallen kann. LABEL Ein Label besteht aus einer Folge von maximal acht Buchstaben und Ziffern, wobei das erste Zeichen ein Buchstabe sein muß. Groß- und Kleinschreibung werden nicht unterschieden. Beginnt ein Label in der ersten Spalte, so kann der Doppelpunkt entfallen. Folgt auf ein Label keiner der PSEUDO-OPCODES 'EQU', 'SET', 'DEFL' bzw. 'DL' oder 'EXT', so erhält es als Wert die relative Adresse des Programmzählers (PC). Nach der Assemblierung werden alle Labels zusammen mit ihren zugeord- neten Werten im PRN-File aufgelistet. Dabei bedeuten: *nnnn relative Adresse (Programmanfang+nnnn) =nnnn absoluter Wert (Adresse oder Zahl) EXTERN externe Adresse MACRO Makro-Anweisung OPCODE Dies kann ein gültiger Prozessor-Opcode sein oder einer der später besprochenen Pseudo-Opcodes (=Pseudo-Ops). Wenn der Opcode entfällt, dürfen auch keine Argumente vorhanden sein. Dem Opcode muß mindestens ein Trennzeichen (Leerzeichen oder Tab) vorausgehen. ARGUMENT Das können prozessorspezifische Angaben wie Register etc. oder Aus- drücke sein. Zudem kann ein Argument auch aus einer ganzen Liste von Ausdrücken bestehen, die jeweils durch Kommata getrennt sind. In man- chen Fällen sind auch Strings zugelassen. Das sind in einfache Anfüh- rungszeichen eingeschlossene Zeichenketten. Das erste Argument muß vom Opcode durch mindestens ein Leerzeichen oder Tab getrennt sein. KOMMENTAR Er wird vom Assembler einfach überlesen und dient allein der Dokumen- tation des Quelltextes. Ausdrücke Ausdrücke repräsentieren stets 16-Bit-Werte. Sie können aus Zahlen und Zeichenkonstanten gebildet werden. Zeichenkonstanten können aus einem oder zwei druckbaren Zeichen bestehen. Im ersten Fall wird der ASCII- Wert zugrunde gelegt. Im zweiten Fall ergibt sich eine 16-Bit-Zahl, deren höherwertiges Byte der ASCII-Wert des ersten und deren nieder- wertiges Bytes der ASCII-Wert des zweiten Zeichens ist. Zahlen beginnen immer mit einer Ziffer ('0'-'9'). Die Basis wird durch einen angefügten Buchstaben festgelegt: B = Binärzahl O, Q = Oktalzahl H = Hexadezimalzahl D oder nichts = Dezimalzahl Zur Bildung komplexerer Ausdrücke stehen folgende Operatoren zur Ver- fügung: LO, HI (niederwertiges, höherwertiges Byte) *, /, MOD (Mal, Ganzzahlteilung, Rest) SHL, SHR (Links-, Rechtsschieben wie in Turbo) +, - (Plus, Minus) NOT (bitweises NOT wie in Turbo) AND, OR, XOR (bitweises UND, ODER, EXCLUSIV-ODER wie in Turbo) <>, <, > (ungleich, kleiner, größer) Besonderheiten: Zur Änderung der Prioritäten kann ein Ausdruck beliebig geklammert werden. Mit dem Dollarzeichen kann auf den Programmzähler zugegriffen werden (Wie bei ASM). Negative Vorzeichen sind zugelassen, positive nicht. Über- oder Unterlauf werden nicht erkannt und führen zu keiner Fehler- meldung. FALSCH und WAHR werden durch 0 und 0FFFFH (-1) ausgedrückt (bei Ver- gleichen). Operatoren gleicher Priorität werden von links nach rechts abgearbei- tet. Im Z80-Modus kann es, falls ein Ausdruck direkt in Verbindung mit einem Opcode auftritt (Beispiel LD A,('a'-'A')*2) zu Problemen führen, wenn der Ausdruck links und rechts durch eine Klammer abgeschlossen wird. Dann nämlich ist der Inhalt der Speicheradresse gemeint, die sich aus der Berechnung des Ausdrucks ohne Klammern ergibt. LD HL,('a'-'A') shl (2*4) führt zu einer Fehlermeldung, da hier der unvollständige Ausdruck 'a'-'A') shl (2*4 an die Berechnungsroutine übergeben wird. Abhilfe kann hier z.B. auf folgende Weise geschehen: LD HL,(('a'-'A') shl (2*4)) wenn der Inhalt, oder LD HL,0+('a'-'A') shl (2*4) wenn der Wert gemeint ist. An jeder Stelle, an der eine Konstante zugelassen ist, kann auch ein Label stehen. Bei den Pseudo-Ops (siehe unten) müssen die Labels be- reits bei der ersten Verwendung definiert sein. Ansonsten werden sie erst im zweiten Assemblerdurchgang (PASS 2) ausgewertet. Ausdrücke, in denen externe Labels vorkommen, werden nicht ausgewer- tet, sondern direkt in das INLINE-Statement übernommen. Aus diesem Grund dürfen hier nur die Operatoren '+' und '-' in Verbindung mit Zahlenkonstanten verwendet werden. Pseudo-Opcodes ORG definiert normalerweise den Anfang eines Programms. INLASS wertet die ORG-Anweisung aber nicht aus, da stets verschiebbarer Code erzeugt wird (Es wird nur ein entsprechender Kommentar in das INLINE-File geschrieben). END legt das logische Ende des Quelltextes fest. Alle nach END folgen- den Anweisungen weren nicht mehr bearbeitet. Fehlt die END-Anweisung, so entspricht das logische Ende dem physikalischen Ende der Datei. EXT Syntax : Bezeichner EXT Auf diese Weise gekennzeichnete Label werden unverändert in die INLINE-Anweisung aufgenommen. Dadurch kann auf Bezeichner (Variable, Prozeduren und Funktionen) des Pascal-Programms zurückgegriffen werden (Standardnamen wie 'WRITE' etc. sind jedoch hiervon ausgeschlossen). Wie das beispielsweise bei Prozedur-Parametern funktioniert, sehen sie in den Beispielen SWAP und LINE. DS oder DEFS Syntax: DS (oder DEFS) Ausdruck Der Ausdruck wird berechnet. Sein Wert ergibt die Anzahl von Bytes, für die ein Speicherbereich reserviert wird. INLASS initialisiert den reservierten Speicherbereich mit Nullen. DS 4*256 reserviert unmittelbar 1KByte DB oder DEFB Syntax: DB (oder DEFB) Argumentliste Die Argumentliste besteht aus einer Liste von Ausdrücken und Strings, die jeweils durch Kommatagetrennt sind. Von den Ausdrücken wird das niederwertige Byte abgelegt, bei Strings der jeweilige ASCII-Wert. DB 'Das ist eine Textzeile',13,10,Otto SHR 4 DW oder DEFW Syntax: DW (oder DEFW) Argumentliste Hier besteht die Argumentliste aus einer Liste von Ausdrücken und zweielementigen Stringkonstanten. Es werden jeweils zwei Byte abge- legt, erst das niederwertige und dann das höherwertige Byte. DW 'AB',Otto SHR 4,0a0dh EQU Syntax: Bezeichner EQU Ausdruck Der Ausdruck wird ausgewertet und dem Bezeichner zugewiesen. Die Zu- weisung mit EQU darf im gesamten Programm nur einmal erfolgen. OFFSET EQU 'a'-'A' LD HL,TABELLE+OFFSET SET Syntax: Bezeichner SET Ausdruck Funktioniert im Prinzip genauso wie EQU, nur daß mit SET einem Be- zeichner mehrmals im Programm ein Wert zugewiesen werden kann. LAENGE SET 10 LAENGE SET LAENGE-1 DL oder DEFL Syntax: Bezeichner DL (oder DEFL) Ausdruck Sowohl EQU als auch SET ordnen einem Bezeichner absolute Werte zu. Mit DEFL können auc relative Adressen zugeordnet werden. Tritt bei einer Berechnung eine relative Adresse auf, so wird der Ausdruck insgesamt relativ interpretiert, ansonsten absolut. DL kann mehrmals im Programm verwendet werden. BDOS DL 0005H absolute Adresse TABELLE DL $+40H relative Adresse IF / ELSE / ENDIF Syntax: IF Ausdruck Anweisungsfolge1 ENDIF Mit IF wird eine bedingte Assemblierung eingeleitet. Der Ausdruck wird berechnet. Wenn sein Wert ungleich null ist (WAHR, s.o.), wird die Anweisungsfolge1 übersetzt, ansonsten, falls ELSE vorhanden ist, An- weisungsfolge2. ENDIF schließt die bedingte Assemblierung ab. FALSCH equ 0 WAHR equ not FALSCH GRAFMODE equ WAHR if GRAFMODE ld hl,0ffh ( wird übersetzt ) else ld hl,0 ( wird nicht übersetzt ) endif Die bedingte Assemblierung kann bis zur Tiefe von 255 verschachtelt werden. MACRO Syntax: Bezeichner MACRO Anweisungsfolge ENDM Hiermit wird dem Bezeichner eine ganze Anweisungsfolge zugeordnet. Dabei ist mit Ausnahme von MACRO jeder Opcode zugelassen, insbesondere auch Macro-Aufrufe. Die in der Parameterliste angegebenen Parameter werden bei einem Aufruf durch aktuelle Parameter ersetzt. Da eine reine Textersetzung vorgenommen wird, sind auch Sonderzeichen zugelas- sen. In der Anweisungsfolge benötigte Label können durch Vor- oder Nachstellen von '&' als lokale Label ausgezeichnet werden. Bei der ersten Ausführung wird dann das '&'-Zeichen durch 'A' ersetz, bei der zweiten durch 'B' usw. Um Namenskollisionen auszuschließen, sollten lokale Label möglichst bezihungsreiche Namen erhalten. Beispiel: VERZ MACRO ; Verzögerungsschleife PUSH BC ; BC soll von 0 bis 0FFFFH zählen PUSH AF LD BC,0 VERZL& DEC BC ; Lokales Label LD A,B OR C JR NZ,VERZL& POP AF POP BC ENDM REPEAT MACRO BEFEHL,ANZAHL ; Makro, das sich selbst aufruft IF ANZAHL>0 BEFEHL R_ANZ SET ANZAHL-1 REPEAT BEFEHL,R_ANZ ENDIF ENDM REPEAT VERZ,3 END Der Aufruf von INLASS Bearbeitet werden nur Quelltextfiles mit der Extension '.SOU'. Der Aufruf von INLASS erfolgt dann mit INLASS >. SOURCEFILE ist der Name der Datei mit dem zu übersetzenden Quelltext ohne Extension. Mit der Extension kann das Laufwerk der Quelltext- datei(s), der INLINE-Datei(i) und der PRN-Datei(p) festgelegt wer- den. INLASS TEST.ABB bedeutet beispielsweise: Der Quelltext steht in A:TEST.SOU, der übersetzte Code wird in B:TEST.INL und das Assemblerlisting in B:TEST.PRN abgelegt. Wird als zweiter Parameter 'N' angegeben, so hat das zur Folge, daß der Quelltext nicht als Kommentar in die INLINE-Anweisung aufgenommen wird. Während der Assemblierung aufgetretene Fehler werden im Klartext so- wohl auf dem Bildschirm als auch im PRN-File ausgegeben. Die Fehlermeldungen von INLASS Illegale Ziffern Ziffern passen nicht zur Zahlenbasis (0203B) Fehler in Strings Stringkonstante hat nicht zwei Zeichen ('ABC') Falsche Zeichen In einem Ausdruck sind illegale Zeichen (3%+25) Label nicht vorhanden Illegale Zeichenfolge Es wurde ein Leerstring zur Berechnung übergeben Division durch Null (24 / (2 SHR 3)) Label doppelt definiert Ein normaler Labelname tritt mehrmals auf Bereichsüberschreitung Die Distanz für relative Sprünge ist zu groß Variable doppelt definiert EQU wird mit einem Namen mehrmal verwendet Phasen-Fehler Ein Label hat unerlaubterweise bei PASS2 einen anderen Wert als bei PASS1 Macro-Fehler Bei der Einrichtung eines Macros ist ein Fehler aufgetreten Klammer-Fehler Ein Ausdruck ist nicht korrekt geklammert OPCODE-Fehler Es wurde ein ungültiger Opcode verwendet verschachtelte Macros Macros dürfen nicht verschachtelt werden ELSE ohne IF ENDIF ohne IF IF ohne ENDIF MACRO ohne ENDM 2. Teil: Erweiterter Z80-Befehlssatz: Wie sich wohl mittlerweile herumgesprochen hat, kann der Z80 weitaus mehr, als ihm seinen Entwickler zugetraut haben. Es gibt 422 weitere Befehle. Die Beschreibung dieser Befehle ist z.B. im Info 9 auf Seite 61 zu finden. Alle dort aufgeführten Befehle sind jetzt in INLASS implementiert. Dabei tritt jedoch ein Problem auf. Für die Befehle mit anschließendem Transfer in ein Register existiert keine genormte Syn- tax. Deswegen mußte ich mir selbst etwas einfallen lassen. Folgendes ist dabei herausgekommen: Transfer in ein Register kommt nur bei indizierten Befehlen vor (das sind Befehle, die eine Adresse über eines der Indexregister mit Offset adressieren). Der Transfer wird jetzt durch einen direkt auf die schließende Klammer folgenden Unterstreichungsstrich (Underscore) und dem Zielregister gekennzeichnet. Zwei Beispiele sollen dies demonstrieren: RES 3,(IX+8), Ergebnis nach H : RES 3,(IX+8)_H RR (IY-2), Ergebnis nach A : RR (IY-2)_A Die gleiche Syntax wird übrigens im neuen MONI (Version 2.00) benutzt. Bei den Befehlen mit den Hälften der Indexregister (HX,LX,HY,LY) muß beachtet werden, daß es die Befehle LD HX,H LD HX,L LD H,HX LD L,HX und die entsprechenden anderen Kombination mit den Registern H und L nicht existieren. Sie entsprechen LD HX,HX LD HX,LX und entsprechend. Indizierte Adressierung: INLASS ließ in indizierten Adressen (s.o.) nur die Angabe eines posi- tiven Offset zu, z.B. LD A,(IX+0A0h). Jetzt kann auch ein negativer Offset angegeben werden z.B. INC (IY-2), oder dieser kann völlig ent- fallen z.B. RES 4,(IX), was dann RES 4,(IX+0) bedeutet. Das funktio- niert natürlich auch mit dem erweiterten Befehlssatz (s.o.). 3. Teil: Die Anwendung von INLASS An einem kleinen, aber sehr effektiven Beispiel soll die Arbeitsweise mit INLASS demonstriert werden. Es soll eine Prozedur geschrieben werden, die zwei Variable eines beliebigen Typs miteinander vertauscht: procedure SWAP (VAR X,Y ; Size : integer); VAR-Parameter werden als Zeiger auf Adressen von Variablen, Wertpara- meter als Adressen von (lokalen) Variablen übergeben. Somit bietet sich folgende Assemblerroutine an: ; Routine zum Austausch zweier Variable ; Parameter : var x,y ; size : integer x ext y ext size ext .z80 swap ld hl,(x) ; Zeiger auf erste Variable ld de,(y) ; dito zweite ld bc,(size) ; Länge ld a,b or c jr z,ende ; Ende bereits erreicht? loop1 ld a,(de) ; (DE) -> A ldi ; (HL) -> (DE), HL->HL+1, DE->DE+1, BC->BC-1 dec hl ; HL->HL-1 ld (hl),a ; A->(HL) inc hl ; HL->HL+1 jp pe,loop1 ; Ende erreicht? ende end Speichere diesen Text unter dem Namen 'SWAP.SOU' ab. Dann starte die Übersetzung mit 'INLASS SWAP'. Du erhältst zwei neue Dateien auf der angemeldeten Diskette: SWAP.INL und SWAP.PRN. Das INL-File hat in etwa folgende Gestalt: InLine ( (* ; Routine zum Austausch zweier Variable *) (* ; Parameter : var x,y ; size : integer *) (*X EXT *) (*Y EXT *) (*SIZE EXT *) (* .Z80 *) $2A/X (*SWAP LD HL,(X) ; Zeiger auf erste Varia*) /$ED/$5B/Y (* LD DE,(Y) ; dito zweite *) /$ED/$4B/SIZE (* LD BC,(SIZE) ; Länge *) /$78 (* LD A,B *) /$B1 (* OR C *) /$28/$09 (* JR Z,ENDE ; Ende bereits erreicht?*) /$1A (*LOOP1 LD A,(DE) ; (DE) -> A *) /$ED/$A0 (* LDI ; (HL) -> (DE), HL->HL+1, DE->*) /$2B (* DEC HL ; HL->HL-1 *) /$77 (* LD (HL),A ; A->(HL) *) /$23 (* INC HL ; HL->HL+1 *) /$EA/*+$FFF9 (* JP PE,LOOP1 ; Ende erreicht? *) (*ENDE END *) ) Du mußt vor diese INLINE-Anweisung nur den obigen Prozedurkopf und BEGIN schreiben sowie an ihrem Ende END; anfügen: procedure SWAP (VAR X,Y; SIZE:INTEGER); begin (* hier das obige INLINE-File einkopieren *) end; Damit hast Du eine der schnellsten Vertausch-Prozeduren auf Z80- Systemen. Im PRN-File werden auftretende Fehler direkt im Assemblerlisting ange- zeigt. Am Ende werden sämtliche Variablen alphabetisch aufgelistet: PASS 1: 0 Fehler aufgetreten. PASS 2: *0000 ; Routine zum Austausch zweier Variable *0000 ; Parameter : var x,y ; size : integer *0000 X EXT *0000 Y EXT *0000 SIZE EXT *0000 .Z80 *0000 $2A/X SWAP LD HL,(X) ; Zeiger auf erste Variable *0003 /$ED/$5B/Y LD DE,(Y) ; dito zweite *0007 /$ED/$4B/SIZE LD BC,(SIZE) ; Länge *000B /$78 LD A,B *000C /$B1 OR C *000D /$28/$09 JR Z,ENDE ; Ende bereits erreicht? *000F /$1A LOOP1 LD A,(DE) ; (DE) -> A *0010 /$ED/$A0 LDI ; (HL) -> (DE), HL->HL+1, DE->DE+1, *0012 /$2B DEC HL ; HL->HL-1 *0013 /$77 LD (HL),A ; A->(HL) *0014 /$23 INC HL ; HL->HL+1 *0015 /$EA/*+$FFF9 JP PE,LOOP1 ; Ende erreicht? *0018 ENDE END 0 Fehler aufgetreten. ENDE * 0018 LOOP1 * 000F SIZE : EXTERN SWAP * 0000 X : EXTERN Y : EXTERN 4. Teil: INLASS als Z80-Assembler Viele Anwender von CP/M-80 haben nur den 8080-Assembler ASM, mit dem sie reine Assemblerprogramme erstellen können. In Verbindung mit INLOAD wird INLASS zum normalen Z80-Assembler. Dazu muß jedoch die Assembleroption 'N' angegeben werden, damit der Quelltext nicht als Kommentar im INL-File auftaucht. INLOAD macht dann aus dem INL-File eine ausführbare COM-Datei. Aufruf: INLOAD Filename (ohne Extension) ACHTUNG!! Es dürfen keine externen Labels verwendet werden. Um die Sache etwas zu vereinfachen, habe ich in INLASS eine weitere Option eingebaut. Wird anstelle der Option 'N' als zweiter Parameter ein 'L' angegeben, so wird die Kommentierung mit dem Quelltext unter- drückt und anschließend sofort INLOAD aufgerufen! Dazu muß sich auf der angemeldeten Diskette das File INLOAD.CHN befinden. Der Aufruf erfolgt nur, wenn die Übersetzung des Quelltextes durch INLASS keine Fehler produziert hat. Sind Fehler aufgetreten, so geht INLASS auf Betriebssystemebene zurück. Noch eine Warnung: INLOAD unterstützt nur das von INLASS erzeugte INLINE-Format. Andere INLINE-Formate, insbesondere mit jeglicher Art von Kommentierung, werden nicht oder nicht immer richtig übersetzt. Und jetzt wünsche ich viel Erfolg mit INLASS und INLOAD! Olaf Krumnow